【并发那些事 】创建线程的三种方式
2019-10-17 09:05:50来源:博客园 阅读 ()
【并发那些事 】创建线程的三种方式
创建线程可以说是并发知识中最基础的操作了,JDK 提供的创建线程的方式,如果不包括通过线程池的话,目前有三种形式,它们分别是通过继承 Thread 类,通过实现 Runable 接口,通过 FutureTask。如下图所示
下面整理了一下 3 种方法的具体使用与异同。
创建线程的 3 种方法
1. 继承 Thread
- 创建一个类继承 Thread 并覆盖 run 方法
class MyThread extends Thread {
@Override
public void run() {
String threadName = getName();
for (int i = 0; i < 20; i++) {
System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
}
}
}
- 创建并启动线程
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 20; i++) {
System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
}
整体流程如下:
这里步骤比较简单和清晰
- 创建一个类,这类要继承 Thread
- 覆盖 Thread 的 run 方法,并在此方法中实现你的多线程任务
- 创建这个类的实例
- 调用它的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而不多线程调用)
2. 实现 Runable
- 创建一个类实现 Runable 接口,并覆盖 run 方法
class MyRunable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 20; i++) {
System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
}
}
}
- 创建类的实现,并将它传给 Thread 的构造函数来创建 Thread?
MyRunable myRunable = new MyRunable();
new Thread(myRunable).start();
new Thread(myRunable).start();
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 20; i++) {
System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
}
整体流程如下:
具体步骤如下:
- 创建一个实现了 Runable 接口的类
- 覆盖 run 方法,并在此方法中实现你的多线程任务
- 创建 Runable 接口实现类的实例
- 通过把上步得到的 Runable 接口实现类的实例,传给? Thread 的有参构造函数来创建 Thread 的实例
- 调用上步得来的 Thread 实例的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而不多线程调用)
3. 通过 FutureTask
- 创建一个实现了 Callable
接口的类,并实现call 方法 (T 代表你想要的返回值类型)
class MyCallerTask implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("执行任务开始");
Thread.sleep(3000);
System.out.println("执行任务结束");
return "hello";
}
}
- 创建并启动线程
// 创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new MyCallerTask());
// 启动线程
new Thread(futureTask).start();
System.out.println("其它操作");
try {
// 等待任务执行完,并获得任务执行完的结果
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
整体流程如下:
具体步骤如下:
- 创建一个实现了 Callable
接口的类,这里 T 的类型就是你线程任务想要返回的类型 - 覆盖 call 方法,并在此方法中实现你的多线程任务
- 创建 Callable 接口实现类的实例
- 通过把上步得到的 Callable 接口实现类的实例,传给? FutureTask 的有参构造函数来创建 FutureTask 的实例
- 通过把上步得到的?FutureTask 实例,传给? Thread?的有参构造函数来创建 Thread 的实例
- 调用上步得来的 Thread 实例的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而不多线程调用)
- 如果你还想获得返回值,需要再调用?FutureTask 实例的 get() 方法(这里要注意,get()会阻塞线程)
3种方法的优缺点
通过上述的演示代码,可以看出这 3 种方法,其实各有优缺点
复杂程度
通过代码量与逻辑可以明显感觉出来,第一种直接继承 Thread 最方便,并且其它两种到最后,还是要依赖创建 Thread 才能实现。所以从方便及难易程度来看,可以得到如下结论:
可扩展性
通过演示代码可以看出,只有第一种是通过继承,其它两种是通过实现接口的形式。我们都知道 JAVA 是不允许多继承,但是可以多实现。所以如果使用了第一种方法,就无法再继承别的类了。另外第一种把线程与线程任务冗余在了一起,不利于后期的维护。所以可以得到如下结论:
是否有返回值
从代码中可以很容易看出,只有通过 FutureTask 的方式才有返回值,另外两种均没有,所以得出如下结论
该用哪种方式创建线程
如果要用到返回值,那不用想,肯定只能使用 FutureTask 的方法。如果对于返回值没有要求,那Thread 与? Runable 均可,不过,考虑到可扩展性,最好使用 Runable 的形式。不过,话说回来,如果在真正项目中使用,综合考虑,一般还是最推荐通过线程池去创建。
原文链接:https://www.cnblogs.com/kiwifly/p/11688600.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- eclipse下创建Maven项目(包含webapp目录结构) 2020-06-09
- SpringBoot通过web页面动态控制定时任务的启动、停止、创建 2020-06-09
- 读了这一篇,让你少踩 ArrayList 的那些坑 2020-05-29
- 那些面试官必问的JAVA多线程和并发面试题及回答 2020-05-28
- 【JavaSE】多线程与并发编程(总结) 2020-05-25
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash