前言
本章分为两个议题
- 如何正确敞开线程池
- shutdown 和 shutdownNow 的区别
1. 线程池示例
public class ShutDownThreadPoolDemo {private ExecutorService service = Executors.newFixedThreadPool(10);
public static void main(String[] args) {new ShutDownThreadPoolDemo().executeTask();}
public void executeTask() {for (int i = 0; i < 100; i++) {service.submit(() -> {System.out.println(Thread.currentThread().getName() + "-> 执行");
});
}
}
}
执行后果
pool-1-thread-2-> 执行
pool-1-thread-3-> 执行
pool-1-thread-1-> 执行
pool-1-thread-4-> 执行
pool-1-thread-5-> 执行
pool-1-thread-6-> 执行
...
执行实现之后,主线程会始终阻塞,那么如何敞开线程池呢?本章介绍 5 种在 ThreadPoolExecutor 中波及敞开线程池的办法,如下所示
- void shutdown
- boolean isShutdown
- boolean isTerminated
- boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException
- ListshutdownNow
2.shutdown
第一种办法叫作 shutdown(),它能够平安地敞开一个线程池,调用 shutdown() 办法之后线程池并不是立即就被敞开,因为这时线程池中可能还有很多工作正在被执行,或是工作队列中有大量正在期待被执行的工作,调用 shutdown() 办法后线程池会在执行完正在执行的工作和队列中期待的工作后才彻底敞开。
调用 shutdown() 办法后如果还有新的工作被提交,线程池则会依据回绝策略间接回绝后续新提交的工作。
这段源码地位(jdk 1.8 版本)
java.util.concurrent.ThreadPoolExecutor#execute
public void execute(Runnable command) {if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 线程池中的线程比外围线程数少
if (workerCountOf(c) < corePoolSize) {
// 新建一个外围线程执行工作
if (addWorker(command, true))
return;
c = ctl.get();}
// 外围线程已满,然而工作队列未满,增加到队列中
if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();
// 工作胜利增加到队列当前,再次查看是否须要增加新的线程,因为已存在的线程可能被销毁了
if (! isRunning(recheck) && remove(command))
// 如果线程池处于非运行状态,并且把以后的工作从工作队列中移除胜利,则回绝该工作
reject(command);
else if (workerCountOf(recheck) == 0)
// 如果之前的线程曾经被销毁完,新建一个非核心线程
addWorker(null, false);
}
// 外围线程池已满,队列已满,尝试创立一个非核心新的线程
else if (!addWorker(command, false))
// 如果创立新线程失败,阐明线程池敞开或者线程池满了,回绝工作
reject(command);
}
1373 行 if (! isRunning(recheck) && remove(command)) 如果线程池被敞开,将以后的工作从工作队列中移除胜利,并回绝该工作
1378 行 else if (!addWorker(command, false)) 如果创立新线程失败,阐明线程池敞开或者线程池满了,回绝工作。
3.isShutdown
第二个办法叫作 isShutdown(),它能够返回 true 或者 false 来判断线程池是否曾经开始了敞开工作,也就是是否执行了 shutdown 或者 shutdownNow 办法。
这里须要留神,如果调用 isShutdown() 办法的返回的后果为 true 并不代表线程池此时曾经彻底敞开了,这仅仅代表线程池开始了敞开的流程,也就是说,此时可能线程池中仍然有线程在执行工作,队列里也可能有期待被执行的工作。
4.isTerminated
第三种办法叫作 isTerminated(),这个办法能够检测线程池是否真正“终结”了,这不仅代表线程池已敞开,同时代表线程池中的所有工作都曾经都执行结束了。
比方咱们下面提到的状况,如果此时曾经调用了 shutdown 办法,然而还有工作没有执行完,那么此时调用 isShutdown 办法返回的是 true,而 isTerminated 办法则会返回 false。
直到所有工作都执行结束了,调用 isTerminated() 办法才会返回 true,这示意线程池已敞开并且线程池外部是空的,所有残余的工作都执行结束了。
5.awaitTermination
第四个办法叫作 awaitTermination(),它自身并不是用来敞开线程池的,而是次要用来判断线程池状态的。
比方咱们给 awaitTermination 办法传入的参数是 10 秒,那么它就会陷入 10 秒钟的期待,直到产生以下三种状况之一:
期待期间(包含进入期待状态之前)线程池已敞开并且所有已提交的工作(包含正在执行的和队列中期待的)都执行结束,相当于线程池曾经“终结”了,办法便会返回 true 期待超时工夫到后,第一种线程池“终结”的状况始终未产生,办法返回 false 期待期间线程被中断,办法会抛出 InterruptedException 异样 调用 awaitTermination 办法后以后线程会尝试期待一段指定的工夫,如果在等待时间内,线程池已敞开并且外部的工作都执行结束了,也就是说线程池真正“终结”了,那么办法就返回 true,否则超时返回 fasle。
6.shutdownNow
最初一个办法是 shutdownNow(),它和 shutdown() 的区别就是多了一个 Now,示意立即敞开的意思,不举荐应用这一种形式敞开线程池。
在执行 shutdownNow 办法之后,首先会给所有线程池中的线程发送 interrupt 中断信号,尝试中断这些工作的执行,而后会将工作队列中正在期待的所有工作转移到一个 List 中并返回,咱们能够依据返回的工作 List 来进行一些补救的操作,例如记录在案并在前期重试。
shutdownNow 源码如下:
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();} finally {mainLock.unlock();
}
tryTerminate();
return tasks;
}
interruptWorkers
让每一个曾经启动的线程都中断,这样线程就能够在执行工作期间检测到中断信号并进行相应的解决,提前结束工作
7.shutdown 和 shutdownNow 的区别?
- shutdown 会期待线程池中的工作执行实现之后敞开线程池,而 shutdownNow 会给所有线程发送中断信号,中断工作执行,而后敞开线程池
- shutdown 没有返回值,而 shutdownNow 会返回敞开前工作队列中未执行的工作汇合(List)
原文链接:https://blog.csdn.net/xiewenf…
版权申明:本文为 CSDN 博主「不懂的浪漫」的原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本申明。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)
2. 别在再满屏的 if/ else 了,试试策略模式,真香!!
3. 卧槽!Java 中的 xx ≠ null 是什么新语法?
4.Spring Boot 2.6 正式公布,一大波新个性。。
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!