线程池shoutdown() shutdownNow() awaitTermination() 方法解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_33641359/article/details/88051173

参考https://www.jianshu.com/p/b5e2283e869c

https://blog.csdn.net/u011389515/article/details/80656813

提到线程池shoutdown() shutdownNow() awaitTermination() 方法之前我们先来看一下线程池的几种状态,在Executor框架中定义了几种状态,如下图:

 
 

1、RUNNING

(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

2、 SHUTDOWN

(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 
(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3、STOP

(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4、TIDYING

(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。 
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5、 TERMINATED

(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。 
(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

shoutdown() shutdownNow() awaitTermination() 就是为了在这几种状态之间进行转换;

常见的问题

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或者,等待的时间到达了指定的时间。

代码例子

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * Created by qiuyunjie on 2019/2/26.
 */
public class Huo {


        public static void main(String[] args) throws InterruptedException {

            ExecutorService executor = Executors.newSingleThreadExecutor();
            //任务一
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("核心线程");
                }
            });
            //任务二
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务队列中的线程");
                }
            });
            //任务三
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("shutdown以后提交的任务");
                }
            });




            //调用shutdown()方法 
//            executor.submit(thread);
//            executor.submit(thread1);
//            executor.shutdown();
//            executor.submit(thread2);

//    输出
//    核心线程
//    任务队列中的线程


        //调用shutdownNow()方法
//            executor.submit(thread);
//            executor.submit(thread1);
//            executor.shutdownNow();
//            executor.submit(thread2);

//        输出
//        java.lang.InterruptedException: sleep interrupted	
//        核心线程

        //调用awaitTermination()方法 来监测线程池是否关闭了;
            executor.submit(thread);
            executor.submit(thread1);

            executor.shutdown();
            while (executor.awaitTermination(1 ,SECONDS) == false) {
                Thread.sleep(500);
                System.out.println("线程池未关闭");
            }
            System.out.println("线程池已关闭");

//         输出
//        线程池未关闭
//        线程池未关闭
//        线程池未关闭
//        核心线程
//        任务队列中的线程
//        线程池已关闭


        }


}

这三种方法的源码

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}
  1. 获取线程池的锁,然后调用checkShutdownAccess方法检查每一个线程池的线程是否有可以ShutDown的权限。

  1. 调用advanceRunState函数通过自旋的CAS操作来将ctl中的状态变为SHUTDOWN
  2. 调用interruptIdleWorkers方法,将所有Idle状态的线程都调用interrupt方法,中断线程。而判断idle状态使用Worker中的ReentrantLock来调用tryLock尝试加锁,看Worker线程是否已经获取了锁,如果Worker的锁已经被加了的话,那么tryLock返回的就是false。
  3. 通过onShutDown()方法告知子类,线程池要处于ShutDown状态了。
  4. 解锁完后,调用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;
}

这两个方法的区别有:

  1. shutDownNow方法会返回未完成的任务队列中的任务列表
  2. 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
    }
}
  1. 判断当前线程池是否正在运行,或者当前线程池的状态比TIDYING(整理中)要大(也就是处于TIDYING或者TERMINATED状态),或者当前线程状态处于SHUTDOWN并且任务队列不为空的话,那么就直接return

  1. 如果当前的WorkerCount不为0,那么就会调用interruptedIdleWorkers(true),并且返回
  2. 通过CAS操作将ctl设置成TIDYING,如果设置成功之后就会调用terminated方法, 告知子类,要终止了,终止完之后,就会将ctl的状态设置成TERMINATED,以及workerCount为0。

猜你喜欢

转载自blog.csdn.net/sinat_33641359/article/details/88051173