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子类的实例,即创建了线程对象. ...
随机推荐
- 阿里面试:问springBoot自动装配我这样回答的,面试官对我竖起了大拇指
引言 最近有个读者在面试,面试中被问到了这样一个问题"看你项目中用到了springboot,你说下springboot的自动配置是怎么实现的?"这应该是一个springboot里面 ...
- 有两张表;使用SQL查询,查询所有的客户订单日期最新的前五条订单记录。
客户信息表(c CUSTOM)有以下字段:id.name.mobile 客户订单表(C_ORDER)有以下字段:id.custom_id.commodity.count.order _date Sel ...
- C++编译过程概述
一 ---导读 想象成工厂要产出一个产品的过程,经过流水线上一步一步,不同的人的操作,然后经过整合,就得到了一个完整可用的产品. 二---编译过程图解 三---在linux中编程详解编译过程 当我们在 ...
- netty服务端客户端启动流程分析
服务端启动流程 我们回顾前面讲解的netty启动流程,服务端这边有两个EventLoopGroup,一个专门用来处理连接,一个用来处理后续的io事件 服务端启动还是跟nio一样,绑定端口进行监听,我们 ...
- 【SpringBoot1.x】SpringBoot1.x 开发热部署和监控管理
SpringBoot1.x 开发热部署和监控管理 热部署 在开发中我们修改一个 Java 文件后想看到效果不得不重启应用,这导致大量时间花费,我们希望不重启应用的情况下,程序可以自动部署(热部署). ...
- redis存json数据时选择string还是hash
redis存json数据时选择string还是hash 我们在缓存json数据到redis时经常会面临是选择string类型还是选择hash类型去存储.接下来我从占用空间和IO两方面来分析这两种类型的 ...
- Python列表推导式玩法
前言 列表做为python的基础,是必须学习的语法之一.一些基础的之前已经是反复温习和使用了,今天我们来学习它的进阶版-->列表推导式. 列表推导式: 优点:是将所有的值一次性加载到内存中,相比 ...
- awk -v参数
-v var=val --assign var=val Assign the value val to the variable var, before execution of the progra ...
- 敏感信息泄露 - Pikachu
概述: 由于后台人员的疏忽或者不当的设计,导致不应该被前端用户看到的数据被轻易的访问到. 比如:---通过访问url下的目录,可以直接列出目录下的文件列表;---输入错误的url参数后报错信息里面包含 ...
- Netty中使用的设计模式
创建型 简单工厂 public class DefaultThreadFactory implements ThreadFactory { @Override public Thread newThr ...