深入理解ThreadPoolExecutor第三弹

从源头解析ThreadPoolExecutor第三弹—属性及execute方法详解

首先我们看看ThreadPoolExecutor都有哪些属性:

ThreadPoolExecutor属性

ctl属性是ThreadPoolExecutor中出现的第一个属性,所以我们首先来看看ctl属性的作用。

ctl属性

ctl是线程池中的重要标识,它主要有两个作用:

  1. 线程池的运行状态
  2. 线程池中工作线程数

这里为什么要用一个变量代表线程池中两个重要标识呢,我们可以从如下两点考虑:

  • 首先,我们需要考虑的是多线程中线程安全的问题。在多线程环境中,Java中主要使用synchronized关键字或者CAS+volatile去解决多线程访问共享资源的问题。但是synchronized适合写多读少的场景,而CAS+volatile适合读多写少的场景。在ThreadPoolExecutor中对于ctl的访问正是读多写少的场景,所以这里使用了CAS+volatile的方法解决多线程问题。
    • CAS保证了操作的原子性
    • volatile保证了操作的可见性
  • 当采用CAS+volatile操作时,不得不面对cas操作的缺点:
    • cas在写多读少的情况下,会出现大量的循环,增加CPU的开销
    • cas只能保证单一变量的原子性操作,所以这里使用一个变量标识两种状态。
    • CAS无法解决ABA问题

了解了上述的知识以后,我们才能真正了解ctl的作用。

我们看看ThreadPoolExecutor源码是怎么实现用ctl表示线程池工作线程数量和线程池运行状态的。

以下是涉及上述ctl作用的源码。

	// Integer.SIZE = 32 ==》 COUNT_BITS = 29;
	private static final int COUNT_BITS = Integer.SIZE - 3;
	// 1 << COUNT_BITS ==》 00100000000000000000000000000000
	// (1 << COUNT_BITS) - 1 ==》 00011111 11111111 11111111 11111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    /*
    下面这些变量表示的就是线程池的运行状态
    从他们的二进制表示可以看出来线程池的运行状态是通过高三位来表示的,其余的位都为0
    */
	/*
	-1:11111111 11111111 11111111 11111111
	RUNNING ==》 -1 << 29:11100000 00000000 00000000 00000000
	*/ 
    private static final int RUNNING    = -1 << COUNT_BITS;
	/*
	SHUTDOWN ==》 00000000 00000000 00000000 00000000
	*/
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
	/*
	STOP ==》 00100000 00000000 00000000 00000000
	*/
    private static final int STOP       =  1 << COUNT_BITS;
    /*
	TIDYING ==》01000000 00000000 00000000 00000000
    */
    private static final int TIDYING    =  2 << COUNT_BITS;
	/*
	TERMINATED ==》10100000 00000000 00000000 00000000
	*/
    private static final int TERMINATED =  3 << COUNT_BITS;
	
    // 通过下面的方法对ctl进行拆箱和装箱
	/*
	获取线程池的运行状态,对ctl进行拆箱工作,ctl的高三位表示的正是线程池的运行状态
	我们看看为什么是这样
	
	~CAPACITY:11100000 00000000 00000000 00000000
	ctl & ~CAPACITY操作就保留了ctl高三位的信息,这高三位就代表了线程池的运行状态
	*/
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
	/*
	获取线程池中工作线程的数量,对ctl进行拆箱工作
	CAPACITY:00011111 11111111 11111111 11111111
	ctl & CAPACITY操作就清除了ctl的高三位信息,用剩余的位数的信息表示正在运行的工作线程的数量
	*/
    private static int workerCountOf(int c)  { return c & CAPACITY; }
	// 根据rs和wc产生ctl的初始值
    private static int ctlOf(int rs, int wc) { return rs | wc; }

我们来看看ctl的初始化操作:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl就是根据运行状态running和工作线程数0来初始化的。

看看线程池的各种状态的含义:

状态解释
RUNNING运行态,可处理新任务并执行队列中的任务
SHUTDOW关闭态,不接受新任务,但处理队列中的任务
STOP停止态,不接受新任务,不处理队列中任务,且打断运行中任务
TIDYING整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法
TERMINATED结束态,terminated() 方法已完成

状态转换流程:

image-20210506103152340

其他属性

/*
用于保存任务并将其传递给工作线程的队列
是一种阻塞队列
*/
private final BlockingQueue<Runnable> workQueue;
/*
通过可重入锁控制对Workers的访问,这里是为了防止多线程并发时c
*/
private final ReentrantLock mainLock = new ReentrantLock();
/*
存储线程池的工作线程,只有持有mainLock锁才能访问workers
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/*
用于等待终止的条件
*/
private final Condition termination = mainLock.newCondition();
/*
当前线程池中包含的工作线程的数量,持有mainLock才能访问
*/
private int largestPoolSize;
/*
已完成任务的计数器,仅在终止工作线程时更新,持有mainLock才能访问
*/
private long completedTaskCount;
/*
用于创建线程的工厂,保证了多线程之间的可见性
*/
private volatile ThreadFactory threadFactory;
/*
线程池的决绝策略
*/
private volatile RejectedExecutionHandler handler;
/*
闲置线程的最大存活时间,主要用于销毁超过核心线程数量的那部分线程
当allowCoreThreadTimeOut为false时核心线程不受此参数的影响,否则核心线程也会被销毁
*/
private volatile long keepAliveTime;
/*
控制核心线程是否被超时时间影响的变量
*/
private volatile boolean allowCoreThreadTimeOut;
/*
核心线程数量
*/
private volatile int corePoolSize;
/*
最大线程数量
*/
private volatile int maximumPoolSize;

构造方法

ThreadPoolExecutor有四种构造方法,它们都是重载的构造方法,最终都调用了参数最全面的构造方法.

	// 如果我们不提供拒绝策略,这里默认的拒绝策略就是拒绝执行任务并抛出异常
	private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }


	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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

corePoolSize:核心线程数量,当提交任务时,如果工作线程数量没有达到核心线程数量,就直接创建新的工作线程,否则就直接加入阻塞队列

maximumPoolSize:最大线程数量,线程池允许存在的最大线程数量,也就是当阻塞队列满时,且工作线程数量大于corePoolSize小于maximumPoolSize时,可以直接创建新的工作线程用于执行这个新任务

keepAliveTime:闲置线程的最大生存事件,当非核心线程的闲置时间大于keepAliveTime时,就回收这个线程

unit:时间的单位

workQueue:阻塞队列,当工作线程数量大于corePoolSize时,直接将提交的任务加入阻塞队列

threadFactory:线程工厂,用于生产线程的工厂,如果我们不提供的话,默认通过Executors.defaultThreadFactory()获得线程工厂

handler:拒绝策略,当工作线程数量达到maximumPoolSize且阻塞队列已满时,丢弃任务的策略

execute(Runnable command)方法

该方法用于异步执行提交的任务。提交的任务可能在新的线程中执行也可能在线程池中存在的线程执行。如果要执行的任务无法提交,可能是因为线程池关闭或者线程池到达了最大容量,此时会采用我们定义的拒绝策略来拒绝新提交的任务。

首先来看看execute方法的源码:

	public void execute(Runnable command) {
    	// 如果新提交的任务为null,就抛出异常
        if (command == null)
            throw new NullPointerException();
        /*
         接下来将可能会有三个流程:
         1、如果当前的工作线程数量小于corePoolSize定义的数量,就尝试启动一个新的工作线程去执行这个给定的任务
         2、如果任务成功加入队列,我们需要进行检查是否需要添加一个线程或者进入该方法后线程池是否关闭
         3、如果不能将新的任务加入队列中,我们尝试添加一个新的线程,如果不能启动新的线程,那么就拒绝执行任务
         */
        int c = ctl.get();
        // workerCountOf获取当前的工作线程数量,如果小于corePoolSize的话就执行addWork方法添加工作线程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // isRunning判断线程池当前的状态,并将任务加入阻塞队列,如果成功加入就执行if的逻辑
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次检查线程池是否在执行,如果没有处于running状态就移除并拒绝任务
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果线程池没有处于running状态,检查工作线程的数量是否为0,是的话就添加一个空的新阿成
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果不能成功加入就执行addWorker方法尝试增加工作线程,不成功就拒绝任务
        else if (!addWorker(command, false))
            reject(command);
    }

execute就是用于提交任务的,提交任务的过程中会有以下三种情况:

1、如果当前的工作线程数量小于corePoolSize定义的数量,就尝试启动一个新的工作线程去执行这个给定的任务
2、如果任务成功加入队列,我们需要进行检查是否需要添加一个线程或者进入该方法后线程池是否关闭
3、如果不能将新的任务加入队列中,我们尝试添加一个新的线程,如果不能启动新的线程,那么就拒绝执行任务

addWorker方法

addWorker方法主要用于添加工作线程。

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        // 获取线程池当前的运行状态
        int c = ctl.get();
        int rs = runStateOf(c);
		
        // 如果线程池处于非running状态
        // 且不满足(线程处于shutdown状态并且任务为空并且任务队列不为空)条件,就直接返回
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
		// 自旋
        for (;;) {
            // 获取当前工作线程的数量
            int wc = workerCountOf(c);
            // 如果工作线程数量wc大于CAPACITY,或者根据core判断wc是否大于核心或者最大线程数量,满足任一条件就返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 增加工作线程的数量,并跳出循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
	// 工作线程启动或者添加完成的标志
    boolean workerStarted = false;
    boolean workerAdded = false;
    // 新建一个空的工作线程
    Worker w = null;
    try {
        // 将提交的任务包装成工作线程
        w = new Worker(firstTask);
        // 获取工作线程的线程
        final Thread t = w.thread;
        // 如果工作线程中的线程不为空就继续执行
        // worker使用ThreadFactory创建线程有可能为空
        if (t != null) {
            // 获取线程池的mainLock锁
            final ReentrantLock mainLock = this.mainLock;
            // 加锁,获取共享资源的访问权限
            // 这里为什么要加锁呢?因为我们要访问共享变量largestPoolSize
            mainLock.lock();
            try {
                // 继续检查线程池状态
                int rs = runStateOf(ctl.get());
				// 如果线程池状态为running(因为只有running状态的值小于shutdown)
                // 或者线程池处于中断状态且提交的任务为空则进入处理流程
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    // 检查工作线程包含的线程是否是可启动的
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                    // 如果是的话,就将工作线程加入workers中进行同一管理
                    workers.add(w);
                    // 如果工作线程的数量大于largestPoolSize,就更新largestPoolSize的值
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    // 并标志成功添加工作线程
                    workerAdded = true;
                }
            } finally {
                // 解锁
                mainLock.unlock();
            }
            // 如果成功添加工作线程,就调用当前工作线程的start方法启动,并标志工作线程的线程启动
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 如果线程没有成功启动证明添加工作线程的方法失败了,调用addWorkerFailed方法进行处理
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

// 该方法主要用于在添加失败之后减少工作线程的数量
// 因为addWorker会在添加工作线程之前就增加其数量,所以添加失败后将相应工作线程数量减1
private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 如果Worker对象不为空,则从workers中移除该对象
            if (w != null)
                workers.remove(w);
            // 减少正在工作的线程数量
            decrementWorkerCount();
            // 尝试终止线程
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }
// 当线程池处于shutdown状态并且线程池和队列为空,或者处于stop状态且线程池为空时
// 将线程池状态改为terminated
final void tryTerminate() {
    	// 自旋地改变状态
        for (;;) {
            int c = ctl.get();
            /*
            如果线程池处于running状态,还在运行不需要终止
            或者c处于tidying或terminated状态,此时已经终止了
            或者线程池状态为shutdown并且阻塞队列非空,此时还有任务未处理完不能终止
            直接返回,不需要终止线程池
            */
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            // 如果工作线程的数量不为0,那就中断闲置的线程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
			// 获取mainLock锁
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 设置ctl的值为tidying,也就是当前线程池的状态为tidying,工作线程的数量为0
                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
        }
    }
// 中断闲置的线程
private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 遍历workers,中断worker对应的线程
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

protected void terminated() { }

从源码的分析中可以看出,addWorker方法首先判断线程池的状态,如果线程池处于运行状态,或者当线程池处于关闭状态时,提交的任务为空,阻塞队列不为空时,该方法才会继续向下执行.

然后会判断工作线程的数量,如果不大于核心线程或者最大线程数量,那就增加当前工作线程的数量.

然后会将提交的任务包装成worker对象添加到workers中,如果成功添加了的话,就直接启动该线程执行提交的任务.

添加失败的话就会调用addWorkerFailed方法.

总的来说,addWorker方法会执行两部分内容:

  1. 通过自旋操作,判断能否继续创建新的工作线程,只有通过这部分判断,才能继续下一步
  2. 创建并添加工作线程,此时还会启动工作线程执行任务

这些提交的任务何时才执行的,下一节将会继续讲解。

getTask方法

runWorker是线程池中执行worker的关键方法,这个方法是一个while循环,当提交的任务或者阻塞队列的任务不为空时,需要一直运行线程池中的任务。所以一个Worker一旦执行,调用自己的run方法,接着就会调用runWorker方法一直参与任务的执行。

我们首先看看runWorker使用的getTask方法。

getTask方法是为了从阻塞队列中获取任务,然后交给线程池执行。

private Runnable getTask() {
    // 判断是否超时
    boolean timedOut = false; // Did the last poll() time out?
	// 自旋获取任务
    for (;;) {
        // 获取线程池的运行状态
        int c = ctl.get();
        int rs = runStateOf(c);
		// 如果线程池的状态处于stop、tidying、terminated
        // 并且当rs处于tidying、terminated时,或者阻塞队列空时
        // 结束当前工作线程,减少工作线程的数量
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            // 减少工作线程的数量
            decrementWorkerCount();
            return null;
        }
		// 获取工作线程的数量
        int wc = workerCountOf(c);
		// 判断工作线程是否需要剔除
        // 如果允许核心线程超时执行或者工作线程数大于核心线程数就标识timed为true
        // 当timed为true时:
        // 允许我们在超时时间内获取任务,当阻塞队列为空时,等待指定的时间后如果无法获取任务就返回null
        // 当timed为false时:此时取任务会阻塞,直到阻塞队列中有了新任务
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		/*
            如果工作线程数量大于最大线程数	
            或者 
            工作线程数大于核心线程数并且上一轮从阻塞对了中获取到了空的任务
            并且
            工作线程数量大于1 或者 阻塞队列为空时
            结束当前工作线程
		*/
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            // 如果任务不为空,就直接返回
            if (r != null)
                return r;
            // 如果获取到空任务,就将超时设置为true,下一轮就有可能会超时阻塞
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

runWorker方法

当我们提交任务或者从getTask中获取任务后,runWorker方法会执行这些任务

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 这里首先调用Worker的解锁方法,是为了允许中断
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        // 当任务不为空时执行任务
        // 当任务为空时,调用getTask方法获取任务
        // 这里是一个while循环,当task不为空或者getTask不为空时,这个方法就会一直运行
        while (task != null || (task = getTask()) != null) {
            // 执行任务之前上锁
            w.lock();
            /*
            这个if判断有多重含义:
            如果线程池处于停止状态,确保线程中断
            如果线程池没有处于停止状态,确保线程不会被中断
            这里需要在第二种情况下重新检查状态,以便处理争用,同时清除中断
            (runStateAtLeast(ctl.get(), STOP):当线程处于tidying或者terminated状态时需要中断线程池
            1、Thread.interrupted():线程中断时
            2、runStateAtLeast(ctl.get(), STOP))):线程处于tidying或terminated时
            3、!wt.isInterrupted():并且当前线程没有被中断时
            同时满足这三点也会终止线程
            */
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                // 执行任务前要做的工作,这是ThreadPoolExecutor提供的扩展点
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 执行任务
                    task.run();
                } 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;
                w.completedTasks++;
                // 解锁
                w.unlock();
            }
        }
        // 
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

其他相关方法

判断当前线程池的状态

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

退出worker时执行的一些操作。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 如果woker线程正常退出,那就不会减少线程执行的任务
    // 如果worker线程异常退出,那么completedAbruptly为true,此时减少线程执行的任务
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();
	// 获取锁并上锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 记录已经完成的任务数量,从workers中移除这个工作线程
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        // 解锁
        mainLock.unlock();
    }
	// 尝试终止线程池
    tryTerminate();
	// 判断线程池的状态,如果为tidying或者terminated进入循环
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        // 如果工作线程正常终止
        if (!completedAbruptly) {
            // 允许闲置的核心线程超时存活时时,min为核心线程的数量,否则为0
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果min为0,并且阻塞队列非空,将min置为1
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 如果工作线程的数量大于min就立即返回
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 添加一个空的任务给worker,暂时没搞懂这是为了啥
        addWorker(null, false);
    }
}
// 自旋的方式进行终止
final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            /*
            如果线程池处于running状态
            或者
            线程池处于tidying或terminated时
            或者
            线程池状态为shutdown并且阻塞队列非空时
            直接返回
            */
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            // 如果工作线程的数量不为0,中断闲置的工作线程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
			// 获取锁,对ctl进行操作
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        // 扩展点,这里没有提供具体的实现
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        // 唤醒等待终止条件的任务
                        // 这里就是唤醒awaitTermination方法,因为该方法等待了termination锁
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }
// 中断闲置的线程
private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                // 中断线程时要获取对应的锁,防止此时线程还在执行其他任务
                // 当worker执行任务的时候需要先获取到锁,所以这里加锁后worker就不能再执行了
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

interruptIdleWorkers在中断闲置线程的时候加锁,这样再闲置线程尝试执行任务的时候会因为获取不到锁而执行,这样也就不会发生一个线程正在中断却正在运行其他任务的情况。

shutdown方法

当使用线程池的时候,调用了shutdown()方法后,线程池就不会再接受新的执行任务了。但是在调用shutdown()方法之前放入任务队列中的任务还是要执行的。此方法是非阻塞方法,调用后会立即返回,并不会等待任务队列中的任务全部执行完毕后再返回

public void shutdown() {
    // 获取线程池的全局锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 检查当前线程是否有权限中断线程
        checkShutdownAccess();
        // 将当前线程池的状态置为shutdown
        advanceRunState(SHUTDOWN);
        // 中断闲置的线程
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        // 释放锁
        mainLock.unlock();
    }
    // 尝试终止线程池
    tryTerminate();
}

checkShutdownAccess用于检查当前线程是否有访问所有工作线程的权限,如果没有的话可能会抛出异常

private void checkShutdownAccess() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkPermission(shutdownPerm);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                // 该方法可能抛出异常
                security.checkAccess(w.thread);
        } finally {
            mainLock.unlock();
        }
    }
}

// 将当前线程池的状态改为targetState给定的状态,此时不会影响工作线程的数量

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

shutdownNow

如果调用了shutdownNow方法,则线程池将不再接收新的执行任务,也会将任务队列中存在的任务丢弃,正在执行的worker线程也会立即中断,同时该方法会立即返回,返回当前任务队列中被丢弃的任务列表

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;
}
// interruptWorkers方法用于中断已经开始运行的工作线程
private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
// 将阻塞队列中的任务加入到列表中,并且从阻塞队列中移除对应的任务
private List<Runnable> drainQueue() {
        BlockingQueue<Runnable> q = workQueue;
        ArrayList<Runnable> taskList = new ArrayList<Runnable>();
        q.drainTo(taskList);
        if (!q.isEmpty()) {
            for (Runnable r : q.toArray(new Runnable[0])) {
                if (q.remove(r))
                    taskList.add(r);
            }
        }
        return taskList;
    }

awaitTermination

当线程池调用了awaitTermination方法后,会阻塞调用者所在的线程,直到线程池的状态修改为terminated才返回,或者达到超时时间后返回。

public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    // 超时时间
    long nanos = unit.toNanos(timeout);
    // 获取ReentrantLock并加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 自旋设置线程池状态,设置为terminated,如果设置成功则返回true
        // 如果超时则返回false
        // 然后就超时等待termination条件锁,达到超时时间会返回
        for (;;) {
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

总结

getTask方法用于从阻塞队列中获取任务,这是一个自旋的操作,也就是说如果不是线程池终止或者阻塞队列为空,这个方法就会一直运行,也就会一直获取任务传递给runWorker方法取执行任务。

runWorker方法首先运行worker自带的任务,当任务执行完毕后会置为空,此时会运行阻塞队列中存储的其他任务,如果这个worker属于核心线程的话,那么当allowCoreThreadTimeOut为true时,它可以一直存在直到线程池终止,否则当闲置时间超过阈值后也会被回收,直到全部线程被回收后线程池终止。

processWorkerExit方法用于终止运行完的woker线程,在此期间会记录worker完成的任务数量,加入到总的任务数量中。并尝试终止线程池,此时可能会清除一些闲置的线程。

使用shutdown和shutdownNow方法可以直接关闭线程池,但是两者的执行策略不同:

  • shutdown方法不再接收新的任务,但是会将阻塞队列中包含的任务运行完成
  • shutdownNow方法也不再接收新的任务,而且会中断正在运行的任务,同时会将阻塞队列中未运行的任务返回给调用者
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值