深入理解Java线程池(3):ThreadPoolExecutor 线程池关闭相关方法与操作

shutdown

用于关闭线程池的方法之一。简单来说,会等任务都完成后在关闭线程池。

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();						//全局锁
    try {
    	//查看调用shutdown的线程是否有权限结束线程池中的worker线程
        checkShutdownAccess();
        //变更标志位			
        advanceRunState(SHUTDOWN);		
       	//此方法会调用interruptIdleWorkers方法,尝试中断所有线程。
		//但任务队列不空,worker不会被关闭。
		interruptIdleWorkers();					
        onShutdown(); 						//空实现方法
    } finally {
        mainLock.unlock();
    }
    tryTerminate();							//尝试结束线程池
}
  • 总结:ThreadPoolExecutor如何防止shutdown方法在任务队列尚未完成时关闭线程的?
    在以前的文章中也有描述,这里再次稍微总结一下。
    1 worker在执行任务时(runWorker方法中)使用了自身的AQS不可重入锁,保证不会被interruptIdleWorkers执行中断。
    2 worker在获取任务时(getTask方法中),并没有加锁,若worker被阻塞队列中阻塞,抛出的异常会被trycatch捕获,之后会从新进入新的循环判断是否需要退出。正常获取任务后,回到runworker方法中,会使用interrupted()方法,清空中断的标志位,并据继续执行获取的任务,防止执行任务时阻塞而抛出异常。
    3 即使由于某些特殊原因,造成worker线程被中断,runWorker方法在最后会调用processWorkerExit方法重新创建worker线程。

advanceRunState

保证不会发生状态退化,且使用cas保证一致性

private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

interruptIdleWorkers

shutdown调用,对每个worker进行判定,只有当worker不再执行任务了,才会将其中断
参数onlyOne判断是否只关闭一个worker

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
//worker中有aqs不可重入锁,保证只有worker在获取任务时,才能trylock成功,才会尝试杀死worker线程;
//t.isInterrupted保证线程尚未被中断
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

shutdownNow

shutdownNow将标志位ctl置为stop,并清空队列

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);				//变更标志位
        interruptWorkers();					//中断所有worker
        tasks = drainQueue();				//获取任务队列中的任务
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

interruptWorkers

shutdownNow调用的方法,无论任务队列是否有任务,无论worker是否正在执行任务,都会尝试关闭线程。

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
        //无论worker是否加锁都会被中断,除非worker未开始执行任务(状态为-1)
            w.interruptIfStarted();	
    } finally {
        mainLock.unlock();
    }
}

tryTerminate

此方法会将stop或者shutdown(并且队列已空)状态的线程池 转变为tidying状态。shutdown和shutdownNow等多个删除或关闭worker的方法会调用此方法。

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
//三种情况下,不去执行。
//1 状态为running,既运行时
//2 状态为tidying和terminated线程池中已经没有worker,不需要执行
//3 状态为shutdown,但队列中还有任务未执行,不能执行
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
//尝试中断一个空闲线程,并返回。
//当shutdown方法被调用,worker消耗完任务后,会由于阻塞抛出异常后返回,结束runworker方法,最后调用此tryTerminate方法,关闭一个空闲线程。
        if (workerCountOf(c) != 0) {
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
//线程池中worker已经全部关闭
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { 	//cas变更状态为tidying
                try {
                    terminated();							//空方法
                } finally {
                    ctl.set(ctlOf(TERMINATED, 0));			//变为terminated
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

很多方法都会调用这个方法。作用是尝试关闭一个worker线程,或者当线程都被关闭是将线程池状态变为terminated。若此方法cas改变线程池状态失败,则会再次循环,再次使用cas对线程池状态进行改变。

至此ThreadPoolExecutor主要方法都被分析完毕,附上另外两篇文章:
https://blog.csdn.net/xiaoyuchenCSDN/article/details/83549068
https://blog.csdn.net/xiaoyuchenCSDN/article/details/83615323

你可能感兴趣的:(java8)