当前位置 博文首页 > 龚厂长的博客:java8 ThreadPoolExecutor之shutdown()与shutdown

    龚厂长的博客:java8 ThreadPoolExecutor之shutdown()与shutdown

    作者:[db:作者] 时间:2021-07-26 14:47

    研究ThreadPoolExecutor源码时,发现两个方法:shutdown()和shutdownNow(),从名字上看都是关闭,只是后者多了一个Now,那么它们两个有什么区别呢?
    首先先来看代码:

        public void shutdown() {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                checkShutdownAccess();
                advanceRunState(SHUTDOWN);//通过自旋锁不断的修改线程池状态,直到修改成功为止
                interruptIdleWorkers();
                onShutdown(); // 空实现,子类可以覆盖
            } finally {
                mainLock.unlock();
            }
            tryTerminate();
        }
        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;
        }
    

    先来说共同点:

    1. 都是public的;
    2. 调用这两个方法之后,调用execute()方法添加新任务都会被拒绝;
    3. 都会对线程发出中断;
    4. 执行线程中断和修改状态前,都会加锁;
    5. 如果阻塞队列中没有任务而且没有运行的线程,两个方法都会将线程池状态修改为TERMINATED,这个操作是在tryTerminate()中完成的;
    6. 调用这两个方法都会对正在运行的线程发出中断,但是不会强行销毁线程,因此尽管执行了这两个方法,线程池可能还会运行一段时间。

    下面来看一下两者的不同点

    1. 修改线程池的状态不同,shutdown()将状态修改为SHUTDOWN,shutdownNow()将状态修改为STOP,修改状态时,调用的方法都是相同的:
    	//入参是目标状态
        private void advanceRunState(int targetState) {
            for (;;) {
                int c = ctl.get();
                if (runStateAtLeast(c, targetState) ||
                    ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
                    break;
            }
        }
    
    1. shutdown()对空闲线程发起中断,而shutdownNow()对所有的线程发起中断;
    2. shutdownNow()调用drainQueue()将阻塞队列清空,并且将阻塞队列中的任务返回给调用方,下面是drainQueue()的代码;
        private List<Runnable> drainQueue() {
            BlockingQueue<Runnable> q = workQueue;
            //新建List对象,用于存储阻塞队列中的任务
            ArrayList<Runnable> taskList = new ArrayList<Runnable>();
            q.drainTo(taskList);//清空阻塞队列,将任务放到新建的List中
            if (!q.isEmpty()) {
                for (Runnable r : q.toArray(new Runnable[0])) {
                    if (q.remove(r))
                        taskList.add(r);
                }
            }
            return taskList;
        }
    
    1. 两个方法的最后都调用了tryTerminate(),因为shutdown()不会清空阻塞队列,所以两个方法执行tryTerminate()还有些区别,如果阻塞队列中还有任务,或者还有正在运行的线程,都会造成shutdown()返回,而shutdownNow()只关心是否还有运行的线程,如果有,选择一个线程发起中断便返回了。
        final void tryTerminate() {
            for (;;) {
                int c = ctl.get();
                if (isRunning(c) ||
                    runStateAtLeast(c, TIDYING) ||
                    (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                    return;
                if (workerCountOf(c) != 0) { 
                    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();
                }
            }
        }
    
    cs