对于我们使用的线程池 ThreadPoolExecutor 来说,停止线程池的方法有以下两个:
下面通过代码案例,咱们来了解一下 shutdown() 和 shutdownNow() 方法的具体使用。
我们将线程池核心和最大线程数都设置为 2,任务队列可以存储 10 个任务,一次性添加了 5 个任务,每个任务执行 2s 以上,添加完任务之后执行停止方法,并在 1s 之后尝试添加另一个新任务,如下代码所示:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorShutdownTest {
public static void main(String[] args) {
// 创建线程
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("执行拒绝策略");
}
});
// 添加任务
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
String tName = Thread.currentThread().getName();
System.out.println(tName + ":开始执行任务!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tName + ":结束执行任务!");
});
}
// 停止线程
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 添加新任务
executor.submit(() -> System.out.println("最后一个新任务"));
}
}
以上程序的执行结果如下:
从以上结果可以看出,执行 shutdown() 方法后,程序会等待线程池中的所有任务全部执行完在关闭,再次期间线程池会拒绝加入新任务,并调用线程池的拒绝策略。
如果将 shutdown() 方法换成 shutdownNow() 方法后,以上程序的执行结果如下:
也就是说,调用 shutdownNow() 之后,正在执行的任务会被立即停止,且任务队列中未执行的任务也会被清除,调用 shutdownNow() 方法后新加入的任务会被拒绝,并执行线程池的拒绝策略。
shutdown() 方法执行源码如下:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
该源码执行流程如下:
shutdown() 方法的执行流程如下图所示:
为什么需要关闭线程池?关闭线程池的场景有哪些?说说 shutdownNow() 的执行流程?
本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。