JAVA线程池如何优雅关闭
Effective JAVA 第三版并发部分提起了线程池优雅关闭的问题,意识到之前的线程关闭知识还不完善。讨论如下:
1.shutdown()
- 基本意思是:启动有序关闭,其中先前提交的任务将被执行关闭,但不会接受任何新任务。
如果已经关闭,调用没有额外的作用。
此方法不等待以前提交的任务完成执行。 使用awaitTermination做到这一点。 - shutdown只负责任务关闭,不负责任务是否执行完毕。比如有线程往数据库写数据,当线程池任务shutdown时,数据未完全写入数据库。此时就会造成预期与实际的数据不一致。
- 源码
/** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * * <p>This method does not wait for previously submitted tasks to * complete execution. Use {@link #awaitTermination awaitTermination} * to do that. * * @throws SecurityException {@inheritDoc} */ private final ReentrantLock mainLock = new ReentrantLock(); public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); }
2.awaitTermination(long timeout, TimeUnit unit)
- 基本意思是:在执行shutdown 或shutdownNow后,线程任务执行并未完全终止,如果任务执行完毕返回true,否则返回false 。
- 说白了就是一个线程活动监测器,等所有任务执行完毕,优雅关闭。
- 源码
/** * Returns true if this executor is in the process of terminating * after {@link #shutdown} or {@link #shutdownNow} but has not * completely terminated. This method may be useful for * debugging. A return of {@code true} reported a sufficient * period after shutdown may indicate that submitted tasks have * ignored or suppressed interruption, causing this executor not * to properly terminate. * * @return {@code true} if terminating but not yet terminated */ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (;;) { if (runStateAtLeast(ctl.get(), TERMINATED)) return true; if (nanos <= 0) return false; nanos = termination.awaitNanos(nanos); } } finally { mainLock.unlock(); } }
3.优雅关闭
-
使用
pool.shutdown(); while(!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) { System.out.println("线程池尚未关闭!"); }