总结实现多线程的三种方式。(上图为多线程的生命周期图,本文不涉及,总结的很好,我就贴过来了…)
三种方式
- 通过继承 Thread 类本身;
- 通过实现 Runnable 接口;
- 通过 Callable 和 Future 创建线程。
1,2无返回值,3返回future对象。其实Thread中的run方法调用的是Runnable接口的run方法。Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。
2相对于1灵活性更好,因为接口可以多实现,同时还可以继承其他类,而1无法完成;而且2方便共享资源:同一份资源,多个代理访问。
直接集成Thread类
在Java中负责线程这个功能的是Java.lang.Thread这个类。 可以通过创建Thread的实例来创建新的线程。 每个线程都是通个某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。 通过调用Thread类的start()方法来启动一个线程。
代码大致框架如下:
|
一个小例子,每个线程进行5次打印。
|
某次的运行结果:
|
实现Runnable接口
代码大致框架如下:
|
一个小例子,实现资源共享:三个卖票窗口,一起来卖5张票。
|
某次的运行结果:
|
实现Callable接口
Callable和Runnable的不同
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执行的任务 。Callable和Runnable有几点不同:
- Callable规定的方法是call(),而Runnable规定的方法是run()
- call()方法可抛出异常,而run()方法不能抛出异常
- Callable的任务执行后可返回值,运行Callable任务可得到一个Future对象,而Runnable的任务无返回值。Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
使用思路
通过FutureTask对象
创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
|
某次运行结果:
|
通过Executor
Executor框架是Java5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好。参考:Executor框架与线程池
创建:Callable实现类 +重写call
借助ExecutorService:执行调度服务ExecutorService,获取Future对象
ExecutorService ser = Executors.newFixedThreadPool(2);
Future result = ser.submit(new CallableImpl()) (submit有返回值,execute无返回值)
获取值:result.get()
停止服务:ser.shutdownNow();
|
某次运行的部分结果:
|