Java实现线程的三种方式和区别
Java实现线程的三种方式和区别
Java实现线程的三种方式:
- 继承Thread
- 实现Runnable接口
- 实现Callable接口
区别:
- 第一种方式继承Thread就不能继承其他类了,后面两种可以;
- 使用后两种方式可以多个线程共享一个target;
- Callable比Runnable多一个返回值,并且call()方法可以抛出异常;
- 访问线程名,第一种直接使用this.getName(),后两种使用Thread.currentThread().getName()。
下面我们通过代码来看一下实现和区别:
三种实现:
//1. 继承Thread,重写run()方法
class Thread1 extends Thread {
private int n = 5;
@Override
public void run() {
while(n > 0) {
System.out.println("name:" + this.getName() + ", n:" + n);
n--;
}
}
}
//2. 实现Runnable接口,实现run()方法
class Thread2 implements Runnable {
private int n = 5;
@Override
public void run() {
while(n > 0) {
System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
n--;
}
}
}
//3. 实现Callable接口,实现call()方法,带有返回值和异常
class Thread3 implements Callable<String> {
private int n = 5;
@Override
public String call() throws Exception {
while(n > 0) {
System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
n--;
}
return String.valueOf(n);
}
}
如何使用:
//第一种实现方式
Thread1 t11 = new Thread1();
Thread1 t12 = new Thread1();
Thread1 t13 = new Thread1();
t11.start();
t12.start();
t13.start();
//第二种实现方式
Thread2 t21 = new Thread2();
Thread2 t22 = new Thread2();
Thread2 t23 = new Thread2();
Thread t211 = new Thread(t21);
Thread t212 = new Thread(t22);
Thread t213 = new Thread(t23);
t211.start();
t212.start();
t213.start();
//第三种实现
Thread3 t31 = new Thread3();
Thread3 t32 = new Thread3();
Thread3 t33 = new Thread3();
FutureTask<String> f1 = new FutureTask<>(t31);
FutureTask<String> f2 = new FutureTask<>(t32);
FutureTask<String> f3 = new FutureTask<>(t33);
Thread t311 = new Thread(f1);
Thread t312 = new Thread(f2);
Thread t313 = new Thread(f3);
t311.start();
t312.start();
t313.start();
从代码可以看出以上提到的区别1,3,4。那么区别2共享一个target是什么意思呢?
首先我们看一下上述代码的运行结果,
第一种:
name:Thread-1, n:5
name:Thread-1, n:4
name:Thread-1, n:3
name:Thread-1, n:2
name:Thread-1, n:1
name:Thread-2, n:5
name:Thread-2, n:4
name:Thread-2, n:3
name:Thread-2, n:2
name:Thread-2, n:1
name:Thread-0, n:5
name:Thread-0, n:4
name:Thread-0, n:3
name:Thread-0, n:2
name:Thread-0, n:1
第二种:
name:Thread-4, n:5
name:Thread-4, n:4
name:Thread-4, n:3
name:Thread-3, n:5
name:Thread-5, n:5
name:Thread-3, n:4
name:Thread-4, n:2
name:Thread-4, n:1
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
name:Thread-5, n:4
name:Thread-5, n:3
name:Thread-5, n:2
name:Thread-5, n:1
可以看到,这两种方式的结果一样,都是new了三个线程,每个线程内部循环5次。d第二种方式并没有体现共用同一个target。如果我们将第二种创建线程的方式改为:
//第二种实现方式
Thread2 t21 = new Thread2();
Thread2 t22 = new Thread2();
Thread2 t23 = new Thread2();
Thread t211 = new Thread(t21);
Thread t212 = new Thread(t21);
Thread t213 = new Thread(t21);
t211.start();
t212.start();
t213.start();
看一下运行结果:
name:Thread-4, n:5
name:Thread-4, n:4
name:Thread-4, n:3
name:Thread-4, n:2
name:Thread-4, n:1
name:Thread-3, n:5
name:Thread-5, n:5
可以看到,虽然也启动了3个线程,但是由于共享一个target,n的值改变了,其他两个线程也会知道,所以因此一共循环了5次。但是这里明明是7次啊,这是由于多线程的同步问题,可以给run方法加上synchronized关键字解决:
@Override
public synchronized void run() {
while(n > 0) {
System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
n--;
}
}
运行结果:
name:Thread-3, n:5
name:Thread-3, n:4
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
这里可能有点迷惑,只启动了一个线程啊。其实另外两个线程也启动了,只是这个时候n=0无法进入循环。我们可以加一行打印:
@Override
public synchronized void run() {
System.out.println("进入" + Thread.currentThread().getName() + "线程");
while(n > 0) {
System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
n--;
}
}
可以看到运行结果:
进入Thread-3线程
name:Thread-3, n:5
name:Thread-3, n:4
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
进入Thread-5线程
进入Thread-4线程
Java实现线程的三种方式和区别的更多相关文章
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...
- java创建线程的三种方式及其对比
第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...
- AJPFX总结java创建线程的三种方式及其对比
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...
- java创建线程的三种方式及其对照
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...
- Java中创建线程的三种方式以及区别
在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...
- Java创建线程的三种方式
一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行体. (2)创建Thread子类的实 ...
- Java创建线程的三种形式的区别以及优缺点
1.实现Runnable,Callable Callable接口里定义的方法有返回值,可以声明抛出异常. 继承Callable接口实现线程 class ThreadCall implements Ca ...
- Java终止线程的三种方式
停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作,也就是放弃当前的操作. 在 Java 中有以下 3 种方法可以终止正在运行的线程: 使用退出标志,使线程正常退出,也就是当 run() 方 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
随机推荐
- springboot mvc自动配置(三)初始化mvc的组件
所有文章 https://www.cnblogs.com/lay2017/p/11775787.html 正文 在springboot mvc自动配置的时候,获得了DispatcherServlet和 ...
- 如何画svg路径图
在画路径图之前,首先得在package.json引入2个依赖 废话不多说,直接上代码 <style> .green { position: absolute; } .blue { posi ...
- 用<audio>标签打造一个属于自己的HTML5音乐播放器
上一章节,我们刚刚讲了<video>标签,今晚,我们讲的是<audio>标签,这两个东东除了表示的内容不一样以外,其他的特性相似的地方真的太多了,属性和用法几乎一样,也就说,如 ...
- 【外网不好用】可以尝试添加dns即可解决上不去外网的问题。
可以将IPv4这里的DNS修改成以下内容再尝试上网试试.
- flask小结
http通讯过程 https://www.cnblogs.com/andy9468/p/10871079.html 1.flask开发环境 https://www.cnblogs.com/andy94 ...
- 跟着minium官网介绍学习minium-----(二)
一: 进入minium官方文档 1. 进入minium目录然后运行服务,出现以下提示说明打开成功, 2. 浏览器直接运行http://localhost:3000即可看到效果. 3. 下图为进入网页后 ...
- 【HICP Gauss】数据库 数据库管理(存储过程 函数 对象 近义词 触发器 事务类型)-9
存储过程存储过程在大新数据库系统中,一组为了完成特定功能的SQL语句集 存储在SQL数据库中 优势: SQL进行封装 调用方便 存储过程会进行编译 提升用户执行SQL语句集的速 ...
- 用js刷剑指offer(替换空格)
题目描述 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 牛客网链接 js代码 func ...
- Gradle 的使用
gradle 工具类似于maven工具,但是gradle 减少了maven中的那种使用xml 中大量的配置文件来下载依赖的jar包.而gradle大大简化了,能够很快速的添加依赖.具体关于gradle ...
- n诺挑战赛5题解
Drinking 题意:就是给你n瓶酒的初始伤害值,第几天喝这瓶酒伤害值就是这瓶酒的初始伤害值第几倍,而且他每天喝的瓶数不超过m.要你输出所有的情况,就是他喝(1~n)瓶的伤害值的最小, 思路:就是这 ...