Java中创建线程的三种方式以及区别
在java中如果要创建线程的话,一般有3种方法:
- 继承Thread类;
- 实现Runnable接口;
- 使用Callable和Future创建线程。
1. 继承Thread类
继承Thread类的话,必须重写run方法,在run方法中定义需要执行的任务。
class MyThread extends Thread{
private static int num = 0; public MyThread(){
num++;
} @Override
public void run() {
System.out.println("主动创建的第"+num+"个线程");
}
}
创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。注意,不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。
public class Test {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
} class MyThread extends Thread{
private static int num = 0; public MyThread(){
num++;
} @Override
public void run() {
System.out.println("主动创建的第"+num+"个线程");
}
}
在上面代码中,通过调用start()方法,就会创建一个新的线程了。为了分清start()方法调用和run()方法调用的区别,请看下面一个例子:
public class Test {
public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.run();
}
} class MyThread extends Thread{
private String name; public MyThread(String name){
this.name = name;
} @Override
public void run() {
System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
}
}
运行结果:
从输出结果可以得出以下结论:
1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;
2)虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。
2. 实现Runnable接口
在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。
下面是一个例子:
public class Test {
public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
} class MyRunnable implements Runnable{ public MyRunnable() { } @Override
public void run() {
System.out.println("子线程ID:"+Thread.currentThread().getId());
}
}
Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。
事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。
在Java中,这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。
3. 使用Callable和Future创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
创建并启动有返回值的线程的步骤如下:
- 创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
- 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
- 使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
下面是一个例子:
public class Main { public static void main(String[] args){ MyThread3 th=new MyThread3(); //使用Lambda表达式创建Callable对象 //使用FutureTask类来包装Callable对象 FutureTask<Integer> future=new FutureTask<Integer>( (Callable<Integer>)()->{ return 5; } ); new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程 try{ System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回 }catch(Exception e){ ex.printStackTrace(); } } }
三种创建线程方式对比:
实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:
- 线程只是实现Runnable或实现Callable接口,还可以继承其他类。
- 这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。
- 但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。
- 继承Thread类的线程类不能再继承其他父类(Java单继承决定)。
PS:一般推荐采用实现接口的方式来创建多线程
Java中创建线程的三种方式以及区别的更多相关文章
- Java中创建线程的三种方法以及区别
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用三种方式来创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线 ...
- Java中创建线程的三种方式及其优缺点
1.自定义一个继承Thread的类,由于Java的单继承特性,限制了该类的扩展性. 2.实现Runnable接口,重写run()方法. 3.实现Callable接口,重写call方法.线程执行体可以有 ...
- JAVA中创建线程的三种方法及比较
JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...
- Java中创建线程的两种方式
创建线程的第一种方式: 创建一个类继承Thread 重写Thread中的run方法 (创建线程是为了执行任务 任务代码必须有存储位置,run方法就是任务代码的存储位置.) 创建子类对象,其实就是在创建 ...
- java中终止线程的三种方式
在java中有三种方式可以终止线程.分别为: 1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止. 2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和sus ...
- java中创建线程的几种方法及区别
1,实现Runnable接口创建线程 特点: A:将代码和数据分开,形成清晰的模型 B:线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法 C:有利于保持程序风格的一致性 2,继承Th ...
- JS中创建函数的三种方式及区别
1.函数声明 function sum1(n1,n2){ return n1+n2; }; 2.函数表达式,又叫函数字面量 var sum2=function(n1,n2){ return n1+n2 ...
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...
- java创建线程的三种方式及其对比
第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...
随机推荐
- * -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]’
错误描述: * -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object fr ...
- Win8下枚举任意进程的句柄表。。。(VB6 Code)
添加一个Command1.一个List1,代码: Private Type PROCESS_HANDLE_TABLE_ENTRY_INFO HandleValue As Long HandleCoun ...
- 奇怪吸引子---Qi
奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...
- pytorch中检测分割模型中图像预处理探究
Object Detection and Classification using R-CNNs 目标检测:数据增强(Numpy+Pytorch) - 主要探究检测分割模型数据增强操作有哪些? - 检 ...
- 坚果云无法同步SVN文件夹
把svn的库放在云盘上,同步到本地,以前在金山快盘.360网盘都用得好好的,换坚果云后,想着肯定没问题,结果发现,不行! 新机子上的版本库可以建起来,但检出时报错: Could not open th ...
- 基于redis 实现分布式锁(二)
https://blog.csdn.net/xiaolyuh123/article/details/78551345 分布式锁的解决方式 基于数据库表做乐观锁,用于分布式锁.(适用于小并发) 使用me ...
- 【Mac使用系列】常用软件及快捷键
Mac下配置ss: 下载地址:https://github.com/shadowsocks/shadowsocks-iOS/releases 旧版本:https://blog.csdn.net/vqh ...
- MySQL 聚簇索引&&二级索引&&辅助索引
MySQL非聚簇索引&&二级索引&&辅助索引 mysql中每个表都有一个聚簇索引(clustered index ),除此之外的表上的每个非聚簇索引都是二级索引,又叫辅 ...
- 11G新特性 -- ASM的兼容性
Oracle 11g中,asm同时支持10g和11g数据库.但是asm的版本不能低于数据库的版本. 与兼容性现相关的两个参数: ·compatible.rdbms 支持的最低版本的oracle数据库版 ...
- Ubuntu 13.10 录音有特别大噪音解决办法
现在物理机跑Ubuntu,平常的QQ只能在虚拟机里跑了:起初和别人QQ语音,别人能听到很大的噪音,以为是虚拟机的回音,属于正常现象,结果我用虚拟机里边的录音工具和Ubuntu里的录音工具测试一下,发现 ...