转自:https://www.cnblogs.com/siriusckx/articles/3989057.html
https://www.jianshu.com/p/b5e2283e869c
1、shutdown()
问:shutdown()有什么功能?
答:阻止新来的任务提交,对已经提交了的任务不会产生任何影响。当已经提交的任务执行完后,它会将那些闲置的线程(idleWorks)进行中断,这个过程是异步的。
问:如何阻止新来的任务提交?
答:通过将线程池的状态改成SHUTDOWN,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的。
问:为何对提交的任务不产生任何影响?
答:在调用中断任务的方法时,它会检测workers中的任务,如果worker对应的任务没有中断,并且是空闲线程,它才会去中断。另外的话,workQueue中的值,还是按照一定的逻辑顺序不断的往works中进行输送的,这样一来,就可以保证提交的任务按照线程本身的逻辑执行,不受到影响。
2、shutdownNow()
问:shutdownNow()有什么功能?
答:阻止新来的任务提交,同时会中断当前正在运行的线程,即workers中的线程。另外它还将workQueue中的任务给移除,并将这些任务添加到列表中进行返回。
问:如何阻止新来的任务提交?
答:通过将线程池的状态改成STOP,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的.
问:如果我提交的任务代码块中,正在等待某个资源,而这个资源没到,但此时执行shutdownNow(),会出现什么情况?
答:当执行shutdownNow()方法时,如遇已经激活的任务,并且处于阻塞状态时,shutdownNow()会执行1次中断阻塞的操作,此时对应的线程报InterruptedException,如果后续还要等待某个资源,则按正常逻辑等待某个资源的到达。例如,一个线程正在sleep状态中,此时执行shutdownNow(),它向该线程发起interrupt()请求,而sleep()方法遇到有interrupt()请求时,会抛出InterruptedException(),并继续往下执行。在这里要提醒注意的是,在激活的任务中,如果有多个sleep(),该方法只会中断第一个sleep(),而后面的仍然按照正常的执行逻辑进行。
3、awaitTermination(long timeout,TimeUnit unit)
简单来说,awaitTermination会一直等待,直到线程池状态为TERMINATED或者,等待的时间到达了指定的时间。
4、补充
//runState != RUNNING
System.out.println("-----isShutdown-------:"+tpe.isShutdown());
//state == SHUTDOWN || state == STOP
System.out.println("-----isTerminating----:"+tpe.isTerminating());
//runState == TERMINATED
System.out.println("-----isTerminated-----:"+tpe.isTerminated());
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}
*/
初始化一个有序的关闭,之前提交的任务都会被执行,但是新提交的任务则不会被允许放入任务队列中。如果之前被调用过了的话,那么再次调用也没什么用。这个方法不会等待之前提交的任务完成执行,如果希望的话,则需要调用awaitTermiante
方法。
该函数的代码为:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
- 获取线程池的锁,然后调用
checkShutdownAccess
方法检查每一个线程池的线程是否有可以ShutDown的权限。 - 调用
advanceRunState
函数通过自旋的CAS操作来将ctl中的状态变为SHUTDOWN
- 调用
interruptIdleWorkers
方法,将所有Idle状态的线程都调用interrupt
方法,中断线程。而判断idle
状态使用Worker中的ReentrantLock
来调用tryLock
尝试加锁,看Worker线程是否已经获取了锁,如果Worker的锁已经被加了的话,那么tryLock返回的就是false。 - 通过
onShutDown()
方法告知子类,线程池要处于ShutDown状态了。 - 解锁完后,调用
tryTermiante
的方法尝试终止线程池。
ShutDownNow方法和ShutDown方法差不多。具体看下面的代码
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;
}
这两个方法的区别有:
- shutDownNow方法会返回未完成的任务队列中的任务列表
advanceRunState
方法中传入的是STOP,而不是SHUTDOWN。
而在tryTermiante方法中:
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
- 判断当前线程池是否正在运行,或者当前线程池的状态比
TIDYING(整理中)
要大(也就是处于TIDYING
或者TERMINATED
状态),或者当前线程状态处于SHUTDOWN
并且任务队列不为空的话,那么就直接return - 如果当前的WorkerCount不为0,那么就会调用interruptedIdleWorkers(true),并且返回
- 通过CAS操作将ctl设置成
TIDYING
,如果设置成功之后就会调用terminated
方法, 告知子类,要终止了,终止完之后,就会将ctl的状态设置成TERMINATED
,以及workerCount为0。
PS:在tryTerminate
函数中,会有判断如果是SHUTDOWN
状态的话,那么就需要判断任务队列是否为空,如果为空的话,那么就会接着往下走,如果任务队列不为空的话,那么说明还有任务没有执行完,直接返回了。在最后一个Worker运行结束后,调用的processWorkerExit函数中还会调用tryTerminate
,所以到那时候,才是真正的结束。
而在tryTeminate后的一句if(workerCountOf(c)!=0)
成立的话,也就是说明任务队列空了,但是Worker还有多的,那么就将最多一个处于IDLE状态的Worker中断。这一段没有太明白具体要干什么。不理解。
至此,整个线程池就完全退出了。
作者:None_Ling
链接:https://www.jianshu.com/p/b5e2283e869c
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。