深入浅出 线程池 shutDown 和 shunDownNow的区别

前言

本文接上篇线程池的源码分析,上篇主要通过实例且围绕源码分析了 线程池的 线程复用、线程回收以及执行原理。由于上篇文章的篇幅太长了,所以才有了本姊妹篇。
深入浅出线程池的源码

一、shutDown 和 shutDownNow 的区别
1. shutDown 方法会把线程池的状态置为 SHUT_DOWN,shutDownNow 将线程池的状态设置为 STOP
2. shutDown 不会中断正在运行执行任务的线程,而 shutDownNow 则会调用 interrupt() 方法来中断阻塞的任务。阻塞的任务 表示的是任务中调用了 sleep、wait等方法使执行任务的线程阻塞。下面会具体分析
3. 调用shutDown后阻塞队列中的任务还是会被执行,而shutDownNow 则不会执行阻塞队列中的任务了。
二、shutDown 和 shutDownNow的源码分析
1. shutDown 源码:
   public void shutdown() {
       
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
           // 检查调用者是否有权限来 shut dwon 线程,这个不用管
            checkShutdownAccess();
            // 设置线程池的状态为 SHUTDOWN,且设置 ctl 的值
            advanceRunState(SHUTDOWN);
            // 尝试中断正在执行任务的线程,里面的逻辑是调用AQS的 tryLock
            // 由于正在执行任务的线程开始的时候都调用了 lock,所以tryLock返回false
            // 即不会调用中断方法 interrupt
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 主要目的是把线程池调用过 shutDown 或者 shutDownNow方法后,
        // 将线程池的状态转变为 TERMINATED
        tryTerminate();
    }

下面逐一分析下 advanceRunStateinterruptIdleWorkerstryTerminate

advanceRunState:

    private void advanceRunState(int targetState) {
        // assert targetState == SHUTDOWN || targetState == STOP;
        for (; ; ) {
            int c = ctl.get();
            int tct = workerCountOf(c); // 获取线程池中创建的线程个数
            // 位运算 或 操作
            int ctc = ctlOf(targetState, tct);
            // 判断线程池的状态是否为SHUT_DOWN 或 STOP,
            // 如果不是则将线程池的状态设置为SHUT_DOWN 或 STOP
            if (runStateAtLeast(c, targetState) 
            || ctl.compareAndSet(c, ctc))
                break;
        }
    }

interruptIdleWorkers:

   private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
    }

interruptIdleWorkers(boolean onlyOne) 的代码如下:

   private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
   		    // workers 里面存放的是正在执行任务的线程集合,
   		    // 开始执行任务的时候会把work加进去
            for (Worker w : workers) {
                Thread t = w.thread;
                // 如果任务没有被中断且通过tryLock获取锁成功,则执行线程的中断
                // 前面也分析过,任务开始执行的时候都会调用lock方法,结束才release,
                // 所以如果任务没有执行完,tryLock获取锁会失败
                // 注意,AQS的lock会阻塞当前线程,而tryLock则不会阻塞,会立刻返回结果;
                // 这也是为什么 shunDown无法中断正在执行的任务。
                if (!t.isInterrupted() && w.tryLock()) {                
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

tryTerminate :

   final void tryTerminate() {
        for (; ; ) {
            int c = ctl.get();  
            // 条件1:线程池是运行状态
            // 条件2:线程池的状态至少是 TIDYING 且阻塞队列不为空
            // 满足就直接return了    
            if (isRunning(c) ||
                    runStateAtLeast(c, TIDYING) ||
                    (runStateOf(c) == SHUTDOWN && !workQueue.isEmpty())) {          
                return;
            }
            // 如果当前线程池中至少有一个线程,那么尝试中断1个正在执行任务的线程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 设置线程池的状态为 TIDYING
                boolean flag = ctl.compareAndSet(c, ctlOf(TIDYING, 0));
                // 如果上面设置状态为TIDYING成功,则把线程池设置为最后状态TERMINATED
                if (flag) {
                    try {
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

从上面的代码分析可知:

  1. shutDown 方法 将 线程池的状态设置为 SHUT_DOWN;
  2. shutDown 方法不会中断正在执行的任务;
  3. 得不出能够继续执行阻塞队列中任务的结论。这个结论可以从 getTask中得出结论。这个可以从上篇的源码分析中得出。
1. shutDownNow 源码:
    public List<Runnable> shutdownNow() {
        
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 设置线程池的状态为Stop,分析见上面的代码
            advanceRunState(STOP);
            // 中断正在执行任务的线程,这个方法是可以执行中断方法的,见下面分析
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        // 见上面分析
        tryTerminate();
        return tasks;
    }

这里的 advanceRunState 方法传入的参数是 STOP,主要分析下 interruptWorkers ,如下:

    private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }

然后调用的是 Worker 类中的 interruptIfStarted 方法,如下:

       void interruptIfStarted() {
            Thread t;
           
            if (getState() >= 0 && (t = thread) != null
             && !t.isInterrupted()) { // **注1**
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

来分析下代码中的 if 条件 if (getState() >= 0 && (t = thread) != null && !t.isInterrupted())

  1. getState()调用的是AQS中的方法,因为Worker类继承了AQS;

  2. getState() > 0,表示的是有线程获取到锁了;
    因为开始执行任务的时候,会调用 w.lock()方法, 这个方法会把state的值加1,所以getState()>0 成立。

  3. thread 表示的是正在执行任务的线程,肯定不为null,条件成立,t.isInterrupted() 表示此线程是否被中断,基本上为false, 除非在在任务中自己调用 Thread.currentThread.interrupt()

综上所分析 ,这个if条件是成立的,所以会执行 t.interrupt(); 上面在提到 shutDownshutDownNow在是否中断正在执行任务的线程时说到:

shutDownNow 则会调用 interrupt() 方法来中断阻塞的任务

这里说明下,interrupt方法只会中断阻塞的线程,什么意思呢?

如果你的任务 Runnable 任务中调用了阻塞的方法,如 sleep、wait等,如下:

        Runnable runnable2 = () -> {           
            try {
                Thread.sleep(3_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }         
        };

在 调用 shutDownNow 方法后,就会抛出中断异常,结束正在执行的任务,即中断了正在执行任务的线程。

如果 你的任务中不会调用阻塞线程的方法,如下:

        Runnable runnable1 = () -> {
            long i = 0;
           while (true){
              Log.e("test thread pool", "r1 start i = "+(i++));            
           }

那么,就算你调用了 shutDownNow ,该任务也会一直执行下去。

所以分析shutDownNow 方法得出的结论是:

  1. shutDownNow把线程池的状态设置为 STOP
  2. shutDownNow 则会调用 interrupt() 方法来中断阻塞的线程
  3. 同样得不出 shutDownNow 不会执行阻塞队列中的任务,这个可以从 getTask中得出:
    我截取部分 getTask 的代码分析就够了:、
   private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (; ; ) {
            int c = ctl.get();
            int rs = runStateOf(c); // 获取线程池的状态
            // 调用 shutDownNow 后,线程池的状态是 STOP ,所以rs >= SHUTDOWN成立
            // 所以rs >= STOP成立,因为  STOP > SHUTDOWN (=0)。即返回null,
            // 返回null就不会再去阻塞队列中获取任务啦
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { 
                decrementWorkerCount();
                Log.e("test thread pool", "--getTask  c = " + ctl.get());
                return null;
            }

           。。。。。。
        }
    }

好嘞,线程池系列完结啦。欢迎交流指正。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值