JAVA线程池shutdown和shutdownNow的区别
2016-04-07 23:33
537 查看
shutDown()
当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
shutdownNow()
根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。
它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
上面对shutDown()以及shutDownNow()作了一个简单的、理论上的分析。如果想知道why,则需要亲自打开JDK源码,分析分析。
想要分析shutDown()以及shutDownNow()源码,我建议首先要对ThreadPoolExecutor有个大概了解。因为关闭线程池的所有方法逻辑都在ThreadPoolExecutor中处理的。
如果你真的想知道为什么,建议看一下我以前写的一篇对ThreadPoolExecutor源码分析的博文,我想这对你比较透彻的了解shutDown()和shutDownNow()的区别以及java 线程池原理有很大的帮助。博文URL:
http://xtu-xiaoxin.iteye.com/admin/blogs/647744
废话少说,要查看源码,首先进入ThreadPoolExecutor的shutDown()方法:
看上面源码,代码1是线程池关闭的关键,如果线程池状态一旦设为SHUTDOWN,则在线程池中会出现两种现象:
1.你不能再往线程池中添加任何任务,否则会抛RejectedExecutionException异常(详细请看ThreadPoolExecutor的addIfUnderCorePoolSize方法)。
2.工作线程Worker获得池队列中的任务时(详细看Worker中的getTask()方法)的处理逻辑也发生了变化:如果线程池为RUNNING状态,并且池队列中没任务时,它会一直等待,直到你提交任务到池队列中,然后取出任务,返回。但是,一旦你执行了shutDown()方法,线程池状态为SHUTDOWN状态,它将不再等待了,直接返回null。如果返回null,则工作线程没有要执行的任务,直接退出(详细看Worker中run()方法)。
代码2是针对这种情况的:在线程池关闭前,有部分工作线程就一直在等着要处理的任务,也就是说工作线程空闲着(这种情况我描述的不好,其实就是Worker正在执行getTask()方法中’ r = workQueue.take();’代码段)。这时,调用interrupt()方法来中断这些Worker线程。进入代码2看看吧。
最后进入shutDownNow()方法看看,这个更简单了,就是设置线程池状态为STOP,然后依次调用工作线程的interrupt()方法,就这么简单,最后还是把源码贴出来吧:
测试代码:
将上例中的shutdown()换成shutdownNow()就可以看到对应的执行结果。所有正在执行的任务全部中断。
并且在调用Thread.sleep(1000*10);时出现了异常。
调用shutdownNow()立即关闭的任务中不能调用sleep()方法,否则会出现java.lang.InterruptedException:
sleep interrupted异常。
原因是主线程启动的主线程很快就结束了,而子线程却sleep一段时间,使得主线程强行打断子线程的sleep,(这里是关闭线程池,强制打断了任务的sleep)因此抛出异常。
当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
shutdownNow()
根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。
它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
上面对shutDown()以及shutDownNow()作了一个简单的、理论上的分析。如果想知道why,则需要亲自打开JDK源码,分析分析。
想要分析shutDown()以及shutDownNow()源码,我建议首先要对ThreadPoolExecutor有个大概了解。因为关闭线程池的所有方法逻辑都在ThreadPoolExecutor中处理的。
如果你真的想知道为什么,建议看一下我以前写的一篇对ThreadPoolExecutor源码分析的博文,我想这对你比较透彻的了解shutDown()和shutDownNow()的区别以及java 线程池原理有很大的帮助。博文URL:
http://xtu-xiaoxin.iteye.com/admin/blogs/647744
废话少说,要查看源码,首先进入ThreadPoolExecutor的shutDown()方法:
public void shutdown() { SecurityManager security = System.getSecurityManager(); if (security != null) security.checkPermission(shutdownPerm); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (security != null) { // Check if caller can modify our threads for (Worker w : workers) security.checkAccess(w.thread); } int state = runState; if (state < SHUTDOWN) //设置线程池状态为关闭状态 runState = SHUTDOWN; //----------------代码1 try { for (Worker w : workers) { //一个一个中断线程 w.interruptIfIdle(); //-----------------代码2 } } catch (SecurityException se) { // Try to back out runState = state; // tryTerminate() here would be a no-op throw se; } tryTerminate(); // Terminate now if pool and queue empty } finally { mainLock.unlock(); } }
看上面源码,代码1是线程池关闭的关键,如果线程池状态一旦设为SHUTDOWN,则在线程池中会出现两种现象:
1.你不能再往线程池中添加任何任务,否则会抛RejectedExecutionException异常(详细请看ThreadPoolExecutor的addIfUnderCorePoolSize方法)。
2.工作线程Worker获得池队列中的任务时(详细看Worker中的getTask()方法)的处理逻辑也发生了变化:如果线程池为RUNNING状态,并且池队列中没任务时,它会一直等待,直到你提交任务到池队列中,然后取出任务,返回。但是,一旦你执行了shutDown()方法,线程池状态为SHUTDOWN状态,它将不再等待了,直接返回null。如果返回null,则工作线程没有要执行的任务,直接退出(详细看Worker中run()方法)。
代码2是针对这种情况的:在线程池关闭前,有部分工作线程就一直在等着要处理的任务,也就是说工作线程空闲着(这种情况我描述的不好,其实就是Worker正在执行getTask()方法中’ r = workQueue.take();’代码段)。这时,调用interrupt()方法来中断这些Worker线程。进入代码2看看吧。
void interruptIfIdle() { final ReentrantLock runLock = this.runLock; /* * 注意这个条件,摆明的就是要等Worker中runTask()方法运行完后才成立。 * 锁机制 */ if (runLock.tryLock()) { try { /* * 如果当前工作线程没有正在运行,则中断线程 * 他能中断工作线程的原因是getTask()方法能抛出一个 * InterruptedException。这时,则可终止那些正在执行 * workQueue.take()方法的工作线程 */ if (thread != Thread.currentThread()) thread.interrupt(); } finally { runLock.unlock(); } } }
最后进入shutDownNow()方法看看,这个更简单了,就是设置线程池状态为STOP,然后依次调用工作线程的interrupt()方法,就这么简单,最后还是把源码贴出来吧:
public List<Runnable> shutdownNow() { /* * shutdownNow differs from shutdown only in that * 1. runState is set to STOP, * 2. all worker threads are interrupted, not just the idle ones, and * 3. the queue is drained and returned. */ SecurityManager security = System.getSecurityManager(); if (security != null) security.checkPermission(shutdownPerm); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (security != null) { // Check if caller can modify our threads for (Worker w : workers) security.checkAccess(w.thread); } int state = runState; if (state < STOP) runState = STOP; try { for (Worker w : workers) { w.interruptNow(); } } catch (SecurityException se) { // Try to back out runState = state; // tryTerminate() here would be a no-op throw se; } List<Runnable> tasks = drainQueue(); tryTerminate(); // Terminate now if pool and queue empty return tasks; } finally { mainLock.unlock(); } }
测试代码:
package com.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * ShutDown关闭线程池测试 * @author hadoop * */ public class ShutDownThreadPool { //固定大小的线程池,同时只能接受5个任务 static ExecutorService mExecutor = Executors.newFixedThreadPool(5); /* * 采用线程池开启多个子线程,主线程等待所有的子线程执行完毕 */ public static void moreThread() { try { int threadNum = 0; /** * 尽管线程池只能同时开启5个线程处理任务,但是10个任务已经放入处理队列中,尽管调用了shutdown方法,已放入的10个任务仍然会执行完成。 * 但是调用了shutdown方法后,就不能再向线程池中添加新任务来了,否则会抛RejectedExecutionException异常 */ for (int i = 0; i < 10; i++) { threadNum++; final int currentThreadNum = threadNum; mExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println("子线程[" + currentThreadNum + "]开启"); Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("子线程[" + currentThreadNum + "]结束"); } } }); } System.out.println("已经开启所有的子线程"); mExecutor.shutdown(); System.out.println("shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。"); int threadNum2 = 0; for (int i = 20; i < 30; i++) { threadNum2++; final int currentThreadNum = threadNum2; mExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println("尝试再次添加任务:子线程[" + currentThreadNum + "]开启"); Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("子线程[" + currentThreadNum + "]结束"); } } }); } /** * 只有执行了shutdown方法,执行isTerminated才有效。否则isTerminated一直为ture */ while(true){ if(mExecutor.isTerminated()){ System.out.println("所有的子线程都结束了!"); break; } Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("主线程结束"); } } public static void main(String[] args) { moreThread(); } }
将上例中的shutdown()换成shutdownNow()就可以看到对应的执行结果。所有正在执行的任务全部中断。
并且在调用Thread.sleep(1000*10);时出现了异常。
调用shutdownNow()立即关闭的任务中不能调用sleep()方法,否则会出现java.lang.InterruptedException:
sleep interrupted异常。
原因是主线程启动的主线程很快就结束了,而子线程却sleep一段时间,使得主线程强行打断子线程的sleep,(这里是关闭线程池,强制打断了任务的sleep)因此抛出异常。
相关文章推荐
- java.lang.IllegalThreadStateException异常原因解析——同一个线程不能重复调用start方法
- [编程题]数独(JAVA)
- openjdk编译
- Java线程池ThreadPoolExecutor简介(一)
- 安卓学习之路 -- JAVA多线程下载
- [JAVA · 初级]:19.容器类
- hibernateTemplate不再出现:spring整合报错getFlushMode is not valid without active transaction
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- Spring Aop
- Spring Boot Web应用的异常处理
- Eclipse调试程序及项目的导入导出
- Java注解
- java 遍历map 方法
- java 抽象类与接口
- 利用java爬虫QDU教务课表
- HashMap的扩容及树化过程
- Java序列化与反序列化总结
- java thread梳理
- Spring4学习(三):注解配置bean
- 【通信15】JAVA OOP授课计划