【并发那些事 】创立线程的三种方式

创立线程可以说是并发知识中最基础的操作了,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<T> 接口的类,并实现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> 接口的类,这里 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 的形式。不过,话说回来,假如在真正项目中使用,综合考虑,一般还是最推荐通过线程池去创立。
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 【并发那些事 】创立线程的三种方式
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 【并发那些事 】创立线程的三种方式