Java并发编程实践 目录
并发编程 04—— 闭锁CountDownLatch 与 栅栏CyclicBarrier
并发编程 06—— CompletionService : Executor 和 BlockingQueue
并发编程 10—— 任务取消 之 关闭 ExecutorService
并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略
并发编程 20—— AbstractQueuedSynchronizer 深入分析
在之前的章节中,看到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 }