并发编程 10—— 任务取消 之 关闭 ExecutorService

时间:2021-03-10 05:18:22

  在之前的章节中,看到ExecutorService提供了两种关闭方法:使用shutdown正常关闭,以及使用shutdownNow 强行关闭。在进行强行关闭时, shutdownNow 首先关闭当前正在执行的任务,然后返回所有尚未启动的任务清单。

  这两种关闭方式的差别在于各自的安全性和响应性:强行关闭的速度更快,但风险也更大,因为任务很可能在执行到一半时被结束;而正常关闭虽然速度慢,但却更安全,因为 ExecutorService 会一直等到队列中的所有任务都执行完成后才关闭。在其他拥有线程的服务中也应该考虑提供类似的关闭方式以供选择。

  简单的程序可以直接在main 函数中启动和关闭全局的 ExecutorService。而在复杂程序中,通常会将 ExecutorService 封装在某个更高级别的服务中,并且该服务能提供自己的生命周期方法,例如下面程序清单中,LogService的一种变化形式,它将管理线程的工作委托给一个 ExecutorService ,而不是由其自行管理。通过封装 ExecutorService,可以将所有权链从应用程序扩展到服务以及线程,所有权链上的各个成员都将管理它所拥有的服务或线程的生命周期。

 

 1 /**
 2  * 7.16 封装ExecutorService实现日志服务
 3  * @ClassName: LogService2
 4  * @author xingle
 5  * @date 2014-11-12 下午4:19:07
 6  */
 7 public class LogService2 {
 8     private final ExecutorService exec = Executors.newSingleThreadExecutor();
 9     private final PrintWriter writer;
10     
11     public LogService2(PrintWriter writer){
12         this.writer = writer;
13     }
14     
15     /**
16      * 产生日志
17      * @param msg 日志内容
18      * @throws InterruptedException
19      */
20     public void log(String msg){
21          exec.execute(new WriteTask(msg));
22     }
23 
24     /**
25      * 停止日志服务
26      * @throws InterruptedException 
27      */
28     public void stop(long timeout, TimeUnit unit) throws InterruptedException {
29         try {
30             exec.shutdown(); // 平缓关闭服务
31             // 关闭服务后, 阻塞到所有任务被执行完毕或者超时发生,或当前线程被中断
32             exec.awaitTermination(timeout, unit);
33         } finally {
34             writer.close();
35         }
36     }
37 }
38 
39 class WriteTask implements Runnable
40 {
41     public WriteTask(String msg) {
42     }
43 
44     public void run()
45     {
46       //do something here
47     }
48 }