ExecutorService 的 shutdown 和 shu

2018-12-15  本文已影响0人  你可记得叫安可

引子

当没有任务需要执行时,ExecutorService 不会自动被系统销毁,而是会继续存活并等待新的任务到来。如果你的 app 需要随时响应处理新提交的任务,那 ExecutorService 的这种生命周期的设计就很合适。但是一个 app 总有结束的时刻,当 app 结束时,ExecutorService 却并不会终止,它将导致 JVM 继续存活并运行。shutdownshutdownNow 就是为关闭 ExecutorService 而设计的 API。

shutdown

shutdown 不会马上销毁 ExecutorService 线程资源,但它会保证其他线程不会再向 ExecutorService 提交新的任务。

executorService.shutdown();

shutdownNow

shutdownNow 会尝试马上销毁 ExecutorService 线程资源,但它不保证 ExecutorService 的线程池中的所有正在运行的线程在同一时刻被终止。shutdownNow 将会返回已在队列中等待被执行的任务。

List<Runnable> notExecutedTasks = executorService.shutDownNow();

区别和联系

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Thread_Shutdown {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        executor.submit(new Runnable() {
    
            @Override
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("interrupted");
                        break;
                    }
                }
            }
        });
    
        executor.shutdown();
        System.out.println("shutdown");
        if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
            System.out.println("Still waiting after 10s: calling System.exit(0)...");
            System.exit(0);
        }
        System.out.println("Exiting normally...");
    }
}

case 1

执行上面这段代码会有如下打印:

shutdown
Still waiting after 10s: calling System.exit(0)...

这是因为 shutdown 方法并不产生设置线程的中断标志,因此在任务的循环中 Thread.currentThread().isInterruped() 始终返回 false

case 2

shutdown 改为 shutdonwNow,打印如下:

interrupted
shutdown
Exiting normally...

这是因为 shutdownNow 会设置任务线程的中断标志,因此代码中任务通过检测到 Thread.currentThread().isInterruped() 会立即退出。

case 3

保持 shutdown,将 while 循环中的代码注释掉,让其一直空循环,打印如下:

shutdown
Still waiting after 10s: calling System.exit(0)...

这是因为任务中不再检查线程的中断标志,因此表现跟 shutdown 一致,并且任务是死循环,因此将一直执行下去,直到程序执行 System.exit(0)

最佳实践

终止 ExecutorService 的一个最佳实践就是,shutdownshutdownNow 两个方法一起,并结合 awaitTermination 来实现超时等待。

executorService.shutdown();
try {
    if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
        executorService.shutdownNow();
    } 
} catch (InterruptedException e) {
    executorService.shutdownNow();
}
  1. 调用 shutdown,阻止新提交任务,并让等待队列中的任务执行完成
  2. 调用 awaitTermination(),保证等待队列中的任务最多执行 800 ms,以防止执行任务时间太长或被阻塞,而导致 ExecutorService 不能被销毁。
  3. awaitTermination 等待 800 ms 后,ExecutorService 中还有任务没执行完,则调用 shutdownNow 强行终止,以释放 ExecutorService 资源。
  4. 结合 Java 中 InterruptedException 的最佳实践 中,上面代码执行 awaitTermination 时所在的线程也有可能被 interrupt,因此需要 catch InterruptedException
上一篇 下一篇

猜你喜欢

热点阅读