java创建线程的多种方式
java创建线程的四种方式
1、继承 Thread 类
通过继承 Thread 类,并重写它的 run 方法,我们就可以创建一个线程。
首先定义一个类来继承 Thread 类,重写 run 方法。
然后创建这个子类对象,并调用 start 方法启动线程。
public class ThreadStudy extends Thread{
/**
* 继承Thread类并且重写run方法,
* 这个方法并不常用,因为一个java类只能继承一个类
*/
@Override
public void run() {
System.out.println("run in sub Thread , Thread name is "+ Thread.currentThread().getName());
}
public static void main(String[] args) {
System.out.println("run in main Thead , Thread name is "+ Thread.currentThread().getName());
ThreadStudy study = new ThreadStudy();
//注意启动线程要用 start()方法
study.start();
try {
//让主线程休眠两秒,让子线程有足够的时间运行
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2、实现 Runnable 接口
通过实现 Runnable ,并实现 run 方法,也可以创建一个线程。
- 首先定义一个类实现 Runnable 接口,并实现 run 方法。
- 然后创建 Runnable 实现类对象,并把它作为 target 传入 Thread 的构造函数中
- 最后调用 start 方法启动线程。
public class ThreadStudy implements Runnable{
/**
* 实现Runnable接口,
* 这个是常用的方法,因为一个java类可以实现多个接口
*/
@Override
public void run() {
System.out.println("run in sub Thread , Thread name is "+ Thread.currentThread().getName());
}
public static void main(String[] args) {
System.out.println("run in main Thead , Thread name is "+ Thread.currentThread().getName());
//要先new 一个Thread
// 然后把实现Runnable接口的类传入到Thread的构造函数中
ThreadStudy study = new ThreadStudy();
Thread thread = new Thread(study);
//注意启动线程要用 start()方法
thread.start();
try {
//让主线程休眠两秒,让子线程有足够的时间运行
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

3.通过匿名类来创建线程,这里使用lambda的写法,也是实际开发中常用的写法
public class ThreadStudy {
public static void main(String[] args) {
System.out.println("run in main Thread , Thread name is "+ Thread.currentThread().getName());
// 不用类实现Runnable接口了, 直接这样定义,方便,省事
Runnable runnable = ()-> System.out.println("run in sub Thread, Thread name is "+Thread.currentThread().getName());
// 启动这个线程还是要用Thread来包装下,然后调用Thread.start()方法
Thread thread = new Thread(runnable);
thread.start();
try {
//让主线程休眠两秒,让子线程有足够的时间运行
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

4、实现 Callable 接口,并结合 Future 实现
首先定义一个 Callable 的实现类,并实现 call 方法。call 方法是带返回值的。
然后通过 FutureTask 的构造方法,把这个 Callable 实现类传进去。
把 FutureTask 作为 Thread 类的 target ,创建 Thread 线程对象。
通过 FutureTask 的 get 方法获取线程的执行结果。
public class TestFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Future<Integer> task = new FutureTask<>(new MyThread());
new Thread((Runnable) task).start();
Integer result = task.get();//获取线程的执行结果,阻塞式
System.out.println(result);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return new Random().nextInt(100);
}
}

5、通过线程池创建线程
此处用 JDK 自带的 Executors 来创建线程池对象。
- 首先,定一个 Runnable 的实现类,重写 run 方法。
- 然后创建一个拥有固定线程数的线程池。
- 最后通过 ExecutorService 对象的 execute 方法传入线程对象。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " thread run....");
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.execute(new MyThread());
}
executorService.shutdown();
}
}

到底有几种创建线程的方式?
那么问题来了,我这里举例了四种创建线程的方式,是不是说明就是四种呢?
我们先看下 JDK 源码中对 Thread 类的一段解释,如下图。

Thrre are two ways to create a new thread of execution
翻译: 有两种方式可以创建一个新的线程
这里说的两种方式就对应我们介绍的前两种方式。
但是,我们会发现这两种方式,最终都会调用 Thread.start 方法,而 start 方法最终会调用 run 方法。
不同的是,在实现 Runnable 接口的方式中,调用的是 Thread 本类的 run 方法。我们看下它的源码,

这种方式,会把创建的 Runnable 实现类对象赋值给 target ,并运行 target 的 run 方法。
再看继承 Thread 类的方式,我们同样需要调用 Thread 的 start 方法来启动线程。由于子类重写了 Thread 类的 run 方法,因此最终执行的是这个子类的 run 方法。
所以,我们也可以这样说。在本质上,创建线程只有一种方式,就是构造一个 Thread 类(其子类其实也可以认为是一个 Thread 类)。
而构造 Thread 类又有两种方式,一种是继承 Thread 类,一种是实现 Runnable接口。其最终都会创建 Thread 类(或其子类)的对象。
再来看实现 Callable ,结合 Future 和 FutureTask 的方式。可以发现,其最终也是通过 new Thread(task) 的方式构造 Thread 类。
最后,在线程池中,我们其实是把创建和管理线程的任务都交给了线程池。而创建线程是通过线程工厂类 DefaultThreadFactory 来创建的(也可以自定义工厂类)。我们看下这个工厂类的具体实现。

它会给线程设置一些默认值,如线程名称,线程的优先级,线程组,是否是守护线程等。最后还是通过 new Thread() 的方式来创建线程的。
因此,综上所述。在回答这个问题的时候,我们可以说本质上创建线程就只有一种方式,就是构造一个 Thread 类。(此结论借鉴来源于 Java 并发编程 78 讲 -- 徐隆曦)
个人想法
但是,在这里我想对这个结论稍微提出一些疑问(若有不同见解,文末可留言交流~)。。。
个人认为,如果你要说有 1种、2种、3种、4种 其实也是可以的。重要的是,你要能说出你的依据,讲出它们各自的不同点和共同点。讲得头头是道,让面试官对你频频点头。。
说只有构造 Thread 类这一种创建线程方式,个人认为还是有些牵强。因为,无论你从任何手段出发,想创建一个线程的话,最终肯定都是构造 Thread 类。(包括以上几种方式,甚至通过反射,最终不也是 newInstance 么)。
那么,如果按照这个逻辑的话,我就可以说,不管创建任何的对象(Object),都是只有一种方式,即构造这个对象(Object) 类。这个结论似乎有些太过无聊了,因为这是一句非常正确的废话。
以 ArrayList 为例,我问你创建 ArrayList 有几种方式。你八成会为了炫耀自己知道的多,跟我说,
- 通过构造方法,
List list = new ArrayList(); - 通过
Arrays.asList("a", "b"); - 通过Java8提供的Stream API,如
List list = Stream.of("a", "b").collect(Collectors.toList()); - 通过guava第三方jar包,
List list3 = Lists.newArrayList("a", "b");
等等,仅以上就列举了四种。现在,我告诉你创建 ArrayList 就只有一种方式,即构造一个 ArrayList 类,你抓狂不。
这就如同,我问你从北京出发到上海去有几种方式。
你说可以坐汽车、火车、坐动车、坐高铁,坐飞机。
那不对啊,动车和高铁都属于火车啊,汽车和火车都属于车,车和飞机都属于交通工具。这样就是只有一种方式了,即坐交通工具。
这也不对啊,我不坐交通工具也行啊,我走路过去不行么(我插眼传送也可以啊,就你皮~)。
最后结论就是,只有一种方式,那就是你人到上海即可。这这这,这算什么结论。。。
所以个人认为,说创建线程只有一种方式有些欠妥。
好好的一个技术文,差一点被我写成议论文了。。。
这个仁者见仁智者见智吧。
最后,我们看一下我从网上看到的一个非常有意思的题目。
有趣的题目
问:一个类实现了 Runnable 接口就会执行默认的 run 方法,然后判断 target 不为空,最后执行在 Runnable接口中实现的 run 方法。而继承 Thread 类,就会执行重写后的 run 方法。那么,现在我既继承 Thread 类,又实现 Runnable 接口,如下程序,应该输出什么结果呢?

可能乍一看很懵逼,这是什么操作。
其实,我们拆解一下以上代码就会知道,这是一个继承了 Thread 父类的子类对象,重写了父类的 run 方法。然后,父对象 Thread 中,在构造方法中传入了一个 Runnable 接口的实现类,实现了 run 方法。
现在执行了 start 方法,必然会先在子类中寻找 run 方法,找到了就会直接执行,不会执行父类的 run 方法了,因此结果为:Thread run 。
若假设子类没有实现 run 方法,那么就会去父类中寻找 run 方法,而父类的 run 方法会判断是否有 Runnable传过来(即判断target是否为空),现在 target 不为空,因此就会执行 target.run 方法,即打印结果:runnable。
所以,上边的代码看起来复杂,实则很简单。透过现象看本质,我们就会发现,它不过就是考察类的父子继承关系,子类重写了父类的方法就会优先执行子类重写的方法。
和线程结合起来,如果对线程运行机制不熟悉的,很可能就会被迷惑。
java创建线程的多种方式的更多相关文章
- Java创建线程四种方式
1.继承Thread类 public class MyThread extends Thread { public MyThread() { } public void run() { for(int ...
- Java并发编程原理与实战五:创建线程的多种方式
一.继承Thread类 public class Demo1 extends Thread { public Demo1(String name) { super(name); } @Override ...
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...
- Java创建线程的三种主要方式
Java创建线程的主要方式 一.继承Thread类创建 通过继承Thread并且重写其run(),run方法中即线程执行任务.创建后的子类通过调用 start() 方法即可执行线程方法. 通过继承Th ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- 操作系统实现线程的几种模式 和 java创建线程的3个方式
操作系统实现线程的几种模式 和 java创建线程的3个方式 这是两个概念 在操作系统中,线程可以实现在用户模式下,也可以实现在内核模式下,也可以两者结合实现. 1.实现线程的三种方式: (1)继承t ...
- AJPFX总结java创建线程的三种方式及其对比
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...
- java创建线程的三种方式及其对照
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...
- Java创建线程的四种方式
Java创建线程的四种方式 1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run方法,run()方法的内容就是该线程执行的内容 创建Thread子类的实例,即创建了线程对象. ...
随机推荐
- JavaScript 函数节流和函数去抖
概念 函数防抖(debounce) 当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间 函数节流(throttle) 预先设定一个执行周期,当调用动作的时刻大于等于执 ...
- select机制
select机制 函数作用: 在一段时间指定的时间内,监听用户感兴趣的文件描述符上可读.可写和异常事件. 函数原型: #include <sys/time.h> #include < ...
- 买卖股票的最佳时机 III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 两笔 交易. 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的 ...
- Linux命令整理,用户管理,用户组管理,系统管理,目录管理常用命令
知识点梳理 Linux课堂笔记 学习目标 能够知道什么是Linux系统以及它的应用场景 能够独立完成安装VMware虚拟机和网络配置 能够独立完成安装CentOS以及远程终端SecureCRT 能够熟 ...
- sql查询速度慢分析及如何优化查询
原因分析后台数据库中数据过多,未做数据优化数据请求-解析-展示处理不当 网络问题提高数据库查询的速度方案SQL 查询速度慢的原因有很多,常见的有以下几种:1.没有索引或者没有用到索引(查询慢最常见的问 ...
- shelll中test命令的使用【转】
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值.字符和文件三个方面的测试. 数值测试 参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt 大于则为真 -ge 大于等于 ...
- python函数1-函数基础
- C语言补码(C语言学习笔记)
记录 在学习C语言数据范围时了解到了补码的概念,记录一下什么是补码,补码怎么运算的 运算 原文链接:https://www.cnblogs.com/lsgsanxiao/p/5113305.html ...
- JVM 判断对象已死,实践验证GC回收
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 提升自身价值有多重要? 经过了风风雨雨,看过了男男女女.时间经过的岁月就没有永恒不变 ...
- 视频画面中实现人脸遮挡教程 - 基于 TensorFlow 实现
在进行视频通话时,我们往往需要对画面进行一些实时分析,例如识别画面里的人.车.动物等等.这节里我们将使用时信魔方的人脸监视模块实现人脸被手部遮挡的检测,如下图所示效果: 预备知识 时信魔方的客户端使用 ...