Java 进阶——多线程优化之线程池 ThreadPoolExecutor源码分析(二)

版权声明:本文为CrazyMo_原创,转载请在显著位置注明原文链接 https://blog.csdn.net/CrazyMo_/article/details/80578319

引言

线程池首先是一种框架,并不是解决某一类的具体的业务功能,本质上是一种“生产者–消费者”模型,上篇Java 进阶——多线程优化之阻塞队列模型和线程池 ThreadPoolExecutor完全使用攻略(一)文章概括地分析了阻塞队列的源码和一些背后的一些主要的核心设计思想,这篇就针对线程池的原理和使用进行总结,部分内容整理摘自《Java 并发编程的艺术》和JDK文档。

一、线程池概述

ThreadPoolExecutor是Java 1.5提供的默认的线程池机制,是一个可以管理线程创建、执行和释放,并通过某种具体的策略实现复用线程去执行新的任务的一个线程管理框架,从而优化了线程的生命周期自动化管理和资源的分配使用。从设计角度来看,这就是典型的**“生产者–消费者”模型,其中当我们把要执行的任务提交到线程池时候相当于生产者产出任务,而线程池执行任务时候就相当于消费者消费任务一般,当然其中的生产和消费细节更为复杂些。通俗理解,所谓线程池,就是用于存放若干线程的一个容器池子,提交一个任务,线程池会自动根据当前的情况采取不同措施去处理,处理完毕之后会自动回收对应的资源,作为使用者无需考虑线程资源的回收和管理**。

二、线程池的创建

Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,无论是什么类型的线程池本质上都是调用下面这个构造方法完成创建的。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • corePoolSize(线程池的基本大小,又称核心池大小)——当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,只有等到需要执行的任务数大于线程池基本大小时才不再创建。但如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程

  • maximumPoolSize(线程池最大大小)——线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。但如果使用了无界的任务队列这个参数就没什么效果。

需要注意的是当核心线程池满且阻塞队列也满时才会判断当前线程数是否小于最大线程数,并决定是否创建新线程。

  • keepAliveTime(线程活动保持时间)——线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。而keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。

  • TimeUnit(线程活动保持时间的单位)——可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

  • workQueue(任务队列)——用于保存等待执行的任务的阻塞队列,不同种类的线程池使用的阻塞队列模型不同。通俗来说,我们构建线程池的时候,无论我们传入队列或者是不传入最后都会自动创建一个队列的,这个队列就是用来缓存那些当我们提交时不能被及时执行的任务(先入列,再等待被执行的)

  • ThreadFactory——用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法,newThread(Runnable r),用来创建线程。

ThreadFactory factory =new ThreadFactory() {
        //线程安全的Integer操作类
            private final AtomicInteger mCount =new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "new Thread #" + mCount.getAndIncrement());
            }
        };
  • RejectedExecutionHandler(饱和策略)——当阻塞队列已满且线程数达到最大值时所采取的饱和策略,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。JDK1.5提供的四种策略:
    >- AbortPolicy——直接抛出异常,throw new RejectedExecutionException(“Task " + r.toString() +” rejected from " +e.toString());
    >- CallerRunsPolicy——只用调用者所在线程来运行任务。既不抛弃任务也不抛出异常,直接运行任务的run方法,换言之将任务回退给调用者来直接运行。使用该策略时线程池饱和后将由调用线程池的主线程自己来执行任务,因此在执行任务的这段时间里主线程无法再提交新任务,从而使线程池中工作线程有时间将正在处理的任务处理完成。
    >- DiscardOldestPolicy——丢弃队列里最近的一个任务,并执行当前任务,即先将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用PriorityBlockingQueue优先级队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。
    >- DiscardPolicy——不做任何处理,丢弃掉提交的任务。

当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略,如记录日志或持久化不能处理的任务。

三、线程池ExecutorService的种类

通过Executor框架的工厂类Executors,可以通过Executors的静态方法来创建四种类型的线程池,一般来说采取默认的饱和策略就可以了,如果需要传入自定义的饱和策略,可以调用对应的重载构造方法通过ThreadFactory参数传入。本质上说就是不同的类型采用了不同的阻塞队列。

1、通过Executors.newFixedThreadPool() 可重用固定线程数的线程池

初始化一个指定线程数的线程池,其中核心线程池大小等于线程池大小,采用LinkedBlockingQueue且容量为Integer。MAX_VALUE,实际现场数量永远维持在mThreads,因此核心线程池大小和线程池大小相当于是无效的,虽然keepAliveTime为0,但当线程池没有可执行任务时,也不会释放线程即keepAliveTime无效。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());//容量为Integer.MAX_VALUE,太奢侈了吧
    }

从源码得知corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。FixedThreadPool执行excute的流程
这里写图片描述

  1. 如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
  2. 在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入 LinkedBlockingQueue。
  3. 线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行。FixedThreadPool使用“无界”队列LinkedBlockingQueue作为线程池的工作队列(队列的容量Integer.MAX_VALUE)会对线程池带来如下影响。
    > 1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。
    2)由于1,使用无界队列时maximumPoolSize将是一个无效参数。
    3)由于1和2,使用无界队列时keepAliveTime将是一个无效参数。
    4)由于使用无界队列,运行中的FixedThreadPool(未执行方法shutdown()或shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。

2、通过Executors.newSingleThreadExecutor() 单个worker线程的线程池

初始化的线程池中有且只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,其实使用装饰模式增强了ScheduledExecutorService的功能,不仅确保只有一个线程顺序执行任务,也保证线程意外终止后会重新创建一个线程继续执行任务。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

这里写图片描述

  1. 如果当前运行的线程数少于corePoolSize(即线程池中无运行的线程),则创建一个新线
    程来执行任务。
  2. 在线程池完成预热之后(当前线程池中有一个运行的线程),将任务加入Linked-
    BlockingQueue。
  3. 线程执行完1中的任务后,会在一个无限循环中反复从LinkedBlockingQueue获取任务来
    执行。

3、通过Executors.newCachedThreadPool() 根据需要创建新线程的线程池

内部采用SynchronousQueue存储等待的任务,这个阻塞队列不存储工作线程,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程,比较适合处理执行时间比较小的任务

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());//不存储的阻塞队列
    }

CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximumPool是“无界“”的。keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。FixedThreadPool和SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工作队列。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。
这里写图片描述

  1. 首先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成;否则执行下面的步骤2)。

  2. 当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行
    SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1)将失败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。

  3. 在步骤2)中新创建的线程将任务执行完后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执行步骤1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。

由于SynchronousQueue是一个没有容量的阻塞队列。每个插入操作必须等待另一个线程的对应移除操作,反之亦然。CachedThreadPool把主线程提交的任务传递给空闲线程执行。CachedThreadPool中任务传递的示意图如图所示:
这里写图片描述

4、通过Executors.newScheduledThreadPool() 延迟运行任务或者定期执行任务的线程池 (调度池)

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,内部使用DelayQueue作为容器。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。一般Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。在实际的业务场景中可以使用该线程池定期的同步数据。

public class Executors {
		public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
		        return new ScheduledThreadPoolExecutor(corePoolSize);
	    }
	    ...
   }

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
	public ScheduledThreadPoolExecutor(int corePoolSize) {
	   super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
	}
    ...
}

调度池内部使用的DelayQueue是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没有什么意义(设置maximumPoolSize的大小没有什么效果)。
这里写图片描述

  1. 当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask。

  2. 线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务

ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改:

  • 使用DelayQueue作为任务队列。
  • 获取任务的方式不同
  • 执行周期任务后,增加了额外的处理。

从源码得知,DelayQueue封装了一个PriorityQueue,用于对队列中的ScheduledFutureTask进行排序。排序时,time小的排在前面(时间早的任务将被先执行)。如果两个ScheduledFutureTask的time相同,就比较sequenceNumber,sequenceNumber小的排在前面(即如果两个任务的执行时间相同,那么先提交的任务将被先执行)。那么ScheduledThreadPoolExecutor中的线程1执行某个周期任务的4个步骤如下图:

这里写图片描述

  1. 线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务
    是指ScheduledFutureTask的time大于等于当前时间。
  2. 线程1执行这个ScheduledFutureTask。
  3. 线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。
  4. 线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。

其中调度池获取任务和添加任务本质上都是操作DelayQueue,其实不只是调度池所有线程池本质上都是在操作各自的阻塞队列,毕竟线程池就是一个“生产者–消费者”模型的一个具体框架应用。

四、线程池的工作原理概述

线程池肯定不应该无限扩大的,否则资源会耗尽,所以当线程数到达一个阶段,提交的任务会被暂时存储在一个队列中,如果队列内容可以不断扩大,极端下也会耗尽资源,那选择什么类型的队列,当队列满如何处理任务,都有涉及很多内容。线程池总体的工作过程如下图:
这里写图片描述
当提交一个新任务到线程池时,线程池的整体处理流程如下:

  1. 线程池判断核心线程池里的线程是否都在执行任务。若没有则创建一个新的工作线程来执行任务;反之则进入下个流程。
  2. 线程池判断工作队列是否已经满。若工作队列没有满,则将新提交的任务存储在这个工作队列里。反之则进入下个流程。
  3. 线程池判断线程池的线程是否都处于工作状态。若没有,则创建一个新的工作线程来执行任务。反之,则交给饱和策略来处理这个任务(当阻塞队列已满且线程数达到最大值时所采取的饱和策略)如下图所示:

这里写图片描述

五、ThreadPoolExecutor重要的成员属性

1、Worker

Worker继承了AbstractQueuedSynchronizer(继承AbstractQueuedSynchronizer以简化获取和释放围绕每个任务执行的锁定)并且实现了Runnable接口,主要维护运行任务的线程的中断控制状态。其实Worker是对firstTask的包装,并且Worker本身就是Runnable的,通过ThreadFactory为Worker自己构建一个线程。而Worker又是Runnable类型的,所以调用线程的start()方法时其实就是执行了Runnable的run()方法。

    /**
     * Class Worker mainly maintains interrupt control state for
     * threads running tasks, along with other minor bookkeeping.
	 * 
     * This class opportunistically extends AbstractQueuedSynchronizer
     * to simplify acquiring and releasing a lock surrounding each
     * task execution.  
	 *
	 * This protects against interrupts that are intended to wake up a worker thread waiting for a task from instead interrupting a task being run. 
	 * We implement a simplenon-reentrant mutual exclusion lock rather than use ReentrantLock because we do not want worker tasks to be able to reacquire the lock 
	 * when they invoke pool control methods like setCorePoolSize.  Additionally, to suppress interrupts until the thread actually starts running tasks, 
	 * we initialize lock state to a negative value, and clear it upon start (in runWorker).
	 * Worker继承了AbstractQueuedSynchronizer(继承AbstractQueuedSynchronizer以简化获取和释放围绕每个任务执行的锁定)并且实现了Runnable接口,主要维护运行任务的线程的中断控制状态
     */
    private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
		...略部分源码
        /** Thread this worker is running in.  Null if factory fails. worker 对应的线程*/
        final Thread thread;
        /** Initial task to run.  Possibly null. worker 对应的第一个任务*/
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker 设置初始状态为-1,禁止中断直到执行ThreadPoolExecutor的runWorker反复噶
            this.firstTask = firstTask;//用传入过来的Runnable初始化
            this.thread = getThreadFactory().newThread(this);//使用线程池工厂创建当前worker的线程
        }

        /** Delegates main run loop to outer runWorker. */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        // 是否被独占,0代表未被独占,1代表被独占
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());//设置独占线程
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); //以独占模式获取,忽略中断}
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            // AQS状态大于等于0并且worker对应的线程不为null并且该线程没有被中断
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

2、ThreadPoolExecutor重要的成员变量

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 线程池的控制状态(用来表示线程池的运行状态(整形的高3位)和运行的worker数量(低29位))
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 29位的偏移量
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 最大容量(1*2^29 - 1)
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    // 线程运行状态,总共有5个状态,需要3位来表示(所以偏移量的29 = 32 - 3)
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
    // 阻塞队列 
    private final BlockingQueue<Runnable> workQueue;
    // 存放工作线程集合
    private final HashSet<Worker> workers = new HashSet<Worker>();
    // 可重入锁
    private final ReentrantLock mainLock = new ReentrantLock();
    // 终止条件
    private final Condition termination = mainLock.newCondition();
    // 最大线程池容量
    private int largestPoolSize;
    // 已完成任务数量
    private long completedTaskCount;
    // 线程工厂
    private volatile ThreadFactory threadFactory;
    // 拒绝执行处理器
    private volatile RejectedExecutionHandler handler;
    // 线程等待运行时间
    private volatile long keepAliveTime;
    // 是否运行核心线程超时
    private volatile boolean allowCoreThreadTimeOut;
    // 核心池的大小
    private volatile int corePoolSize;
    // 最大线程池大小
    private volatile int maximumPoolSize;
    // 默认饱和策略处理器
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
}

##3、线程池的状态控制
在ThreadPoolExecutor源码的注释的第一句就写着这样一句话The main pool control state, ctl已经说明了线程池内部的状态由ctl表示(由于有5种状态,最少需要3位表示,所以采用的AtomicInteger的高3位来表示,低29位用来表示worker的数量,即最多表示2^29 - 1):

其中ctl是线程池的主要控制状态,它是一个AtomicInteger的数值,封装了两部分内容:

  1. workerCount, 表示有效线程数;
  2. runState, 表示线程池状态,是否运行,停止等。
    ctl是用一个Integer(32)来包含线程池状态和数量的表示,高三位为线程池的状态,后三位(2^29)-1为线程数限制(COUNT_BITS,用一个Integer.SIZE-3来作为位数)。这个整数的0-28位表示的就是线程的数目;而高位的部分(即29-31位)表示线程池的状态。

主要定义了五种状态:

  • RUNNING(-1 << COUNT_BITS,即高3位为111)——线程池会接收新任务,并处理阻塞队列中的任务
  • SHUTDOWN(0 << COUNT_BITS,即高3位为000)——线程池不会接收新任务,但会处理阻塞队列中的任务
  • STOP : (1 << COUNT_BITS,即高3位为001)——线程既不接收新任务,也不处理阻塞队列中的任务,而且还会中断正在运行的任务
  • TIDYING ( 2 << COUNT_BITS,即高3位为010)——所有任务都已终止,workerCount为零,转换为状态TIDYING的线程将运行terminate()钩子方法
  • TERMINATED( 3 << COUNT_BITS,即高3位为011)——terminated() 方法执行完毕

内部状态转换:

  • 当调用了shutdown()方法时,状态可能由RUNNING——>SHUTDOWNRUNNING(SHUTDOWN)——>STOP
  • 阻塞队列和池子均为空时,状态由SHUTDOWN——>TIDYING
  • 当池子为空时,状态由STOP——>TIYDING
  • 当terminated()方法被执行完成时候,状态由TYDING——>TERMINATED

#六、线程池的任务提交
无论是可重用固定数量的线程池单个工作线程的线程池缓存线程池调度池的提交都是调用Executor及其子类的方法,本质上说都是对阻塞队列(对应着ThreadPoolExecutor的成员变量workQueue用于存放等待执行的任务的阻塞队列即任务队列集合,在构造方法的时候传入并初始化)和当前的可立即执行线程集合(对应着workers 用于存储所有可以立即执行的作业线程(需要获取锁,因为HashSet不安全)即作业线程集合)的操作,其中线程池框架提供了以下两种方式:

1、通过Executor的execute(Runnable command)方法提交任务并执行

通过Executor的execute(Runnable command)方法,需要传递一个Runnable 对象且无法获取返回值,所以无法判断任务是否已经执行完毕。

public interface Executor {
    /**
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

ThreadPoolExecutor的execute方法作为提供给我们开发者使用主要的入口方法之一,在使用的时候我们只需要把工作线程提交到线程池中就可以了,不必去主动执行和回收,其主要流程如下图所示:

这里写图片描述

首先开发者创建线程池,然后调用ThreadPoolExecutor的execute方法,在内部进行各种条件判断之后,把任务Runnable添加到阻塞队列workQueue和把Worker存到执行工作线程集合workers中,若添加失败就自动执行回滚或执行饱和策略,其中在添加到到workers集合,就基本完成了execute的职责了?但是无论从命名还是第一句注释都说了,还要去执行对应的工作线程,那么是哪里把队列或工作线程集合 取出来执行呢?

    /**
     * Executes the given task sometime in the future.  The task may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of {@code RejectedExecutionHandler}, if the task cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
		 * 如果当前的线程数小于核心线程池的大小,根据现有的线程作为第一个Worker运行的线程,
         * 新建一个Worker,addWorker自动的检查当前线程池的状态和Worker的数量,
         * 防止线程池在不能添加线程的状态下添加线程,
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         * 如果线程入队成功,然后还是要进行double-check的,因为线程池在入队之后状态是可能会发生变化的,因为在前一次检查后
         * 可能该线程死亡了或者当进入到此方法时,线程池已经shutdown了,当停止时还需要回滚入队列操作或者当线程池没有线程时需要创建一个新线程
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
		 * 如果task不能入队(队列满了),这时候尝试增加一个新线程,如果增加失败那么当前的线程池状态变化了或者线程池已经满了然后执行饱和策略
         */
        int c = ctl.get();//获取线程池状态
		//先判断是否小于核心线程池大小
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))//把线程池里所有可以立即执行的线程都添加到workers集合里(需要获取锁)
                return;
            c = ctl.get();
        }
		//如果还是RUNNING 状态,调用阻塞队列的offer方法插入任务到阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {//添加到阻塞队列
            int recheck = ctl.get();
			//添加成功之后,再次检查,因为从上一次check,线程池内部状态发生改变,如果此时线程池不是RUNNING则不宜接收新的任务,因为线程池关闭了我们就拒绝这个任务,如果有线程死亡我们就创建新的线程状态
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)//worker 数量为0,
                addWorker(null, false);//添加新的工作线程,不一定会成功addWorker 内部会自检,失败的话会自动回滚
        }
        else if (!addWorker(command, false))
            reject(command);//添加worker失败,就执行饱和策略
    }
	
	/**
	* 小于SHUTDOWN,则说明线程池内部状态为RUNNING
	*/
	 private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }
	

在addWorker方法内部成功把Worker 添加到待执行工作线程集合之后,会自动调用Worker中的run 方法,而在run 方法中运行 ThreadPoolExecutor的runWorker(this)方法,在runWorker(this)方法里去通过getTask()方法从队列中获取任务并执行它们

    /**
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don't need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread's
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread's UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     * 实际执行给定任务(即调用用户重写的run方法),并且当给定任务完成后,会继续从阻塞队列中取任务,直到阻塞队列为空(即任务全部完成)
     * @param w the worker
     */
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();//当前线程
        Runnable task = w.firstTask;//获取当前Worker的firstRunnable
        w.firstTask = null; //设置w的firstTask为null
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
			//如果当前Worker的firstTask 不为空 或者 阻塞队列中的获取的task 不为空
            while (task != null || (task = getTask()) != null) {
                w.lock();//w以独占模式获取,忽略中断
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) //线程池状态是高于STOP,即是RUNNING或者SHUTDOWN
					||(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted())
                    wt.interrupt();//中断当前线程
                try {
                    beforeExecute(wt, task);//提供来给我们自定义线程池时,用于在运行任务前做一些操作
                    Throwable thrown = null;
                    try {
                        task.run();//开始运行指定的task、
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;//这里置为null 方便GC,用完就置为null
                    w.completedTasks++;
                    w.unlock();//释放独占
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
	
	

其中getTask()方法用于从workerQueue阻塞队列中获取Runnable对象,由于是阻塞队列,所以支持有限时间等待(poll)和无限时间等待(take)。由于线程池的状态可能时刻都在变化,所以还会响应shutDown()和、shutDownNow()方法的操作,若检测到线程池处于SHUTDOWN或STOP状态,则会返回null,而不再返回阻塞队列中的Runnalbe对象,最终触发processWorkerExit()方法。

/**
     * Performs blocking or timed wait for a task, depending on
     * current configuration settings, or returns null if this worker
     * must exit because of any of:
     * 1. There are more than maximumPoolSize workers (due to
     *    a call to setMaximumPoolSize).
     * 2. The pool is stopped.
     * 3. The pool is shutdown and the queue is empty.
     * 4. This worker timed out waiting for a task, and timed-out
     *    workers are subject to termination (that is,
     *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
     *    both before and after the timed wait, and if the queue is
     *    non-empty, this worker is not the last thread in the pool.
     *
     * @return task, or null if the worker must exit, in which case
     *         workerCount is decremented
     */
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();//线程池状态
            int rs = runStateOf(c);//运行状态

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();//递减ctl的workerCount字段
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;// 返回null,不执行任务,该worker会退
                continue;
            }

            try {
				//从阻塞队列里拿出Runnale
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

    /**
     * Performs cleanup and bookkeeping for a dying worker. Called
     * only from worker threads. Unless completedAbruptly is set,
     * assumes that workerCount has already been adjusted to account
     * for exit.  This method removes thread from worker set, and
     * possibly terminates the pool or replaces the worker if either
     * it exited due to user task exception or if fewer than
     * corePoolSize workers are running or queue is non-empty but
     * there are no workers.
     *  处理快要死亡的Worker线程
     * @param w the worker
     * @param completedAbruptly if the worker died due to user exception
     */
    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);//从workers中移除当前worker
        } finally {
            mainLock.unlock();
        }

        tryTerminate();//改变线程池的状态

        int c = ctl.get();//获取线程池的状态
        if (runStateLessThan(c, STOP)) {//小于STOP的状态,STOP对应值为1,那么状态只有是RUNNING或SHUTDOWN才为true
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

2、通过ExecutorService.submit()方法提交的任务并执行

在submit()方法内部,本质上还是会调用到execute函数,所以具体流程不再赘述。具体区别在于提交的任务不需要一个结果的话直接用execute()会提升很多性能,而提交的任务是需要结果的,那就去继承Callable接口,然后传递到submit方法就行了;如果你只需要一个特定的结果,就把那个特定的结果告诉submit方法然后把你想要的特定结果也告诉他,它只是帮你完成以前使用Future模式的时候你自己需要做的那些步骤而已,如果你不需要一个结果,那么就老老实实使用execute;如果你需要的是一个空结果,那么submit(yourRunnable)与submit(yourRunnable,null)是等价的!

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

##3、线程池的关闭
线程池子的关闭主要是通过ThreadPoolExecutor的shutdownNow()和shutdown方法,两者功能上大同小异,区别在于shutdownNow会尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表,但是其会终止所有的worker,而并非空闲的worker。shutdown会设置线程池的运行状态为SHUTDOWN,并且中断所有空闲的worker,由于worker运行时会进行相应的检查,所以之后会退出线程池,并且其会继续运行之前提交到阻塞队列中的任务,不再接受新任务。而shutdownNow则会设置线程池的运行状态为STOP,并且中断所有的线程(包括空闲和正在运行的线程),在阻塞队列中的任务将不会被运行,并且会将其转化为List返回给调用者,也不再接受新任务,其不会停止用户任务(只是发出了中断信号),若需要停止,需要用户自定义停止逻辑。

    /**
     * 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.
     */
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();//检查权限
            advanceRunState(SHUTDOWN);//设置线程池内部状态
            interruptIdleWorkers();//中断空闲Worker
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();//尝试中止被改变状态
    }

    /**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution. These tasks are drained (removed)
     * from the task queue upon return from this method.
     *
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     *
     * <p>There are no guarantees beyond best-effort attempts to stop
     * processing actively executing tasks.  This implementation
     * interrupts tasks via {@link Thread#interrupt}; any task that
     * fails to respond to interrupts may never terminate.
     */
    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;
    }

PS

花了好多天时间总算是把线程池核心流程走了一遍,或许理解有误,仅仅是个人的一些理解和分享,如有误之处还望不吝赐教,篇幅问题下一篇线程池的简单使用放到下一篇文章。

猜你喜欢

转载自blog.csdn.net/CrazyMo_/article/details/80578319
今日推荐