线程池ThreadPoolExecutor源码解读

转载博客链接:http://www.ideabuffer.cn/2017/04/04/深入理解Java线程池:ThreadPoolExecutor/

线程池ThreadPoolExecutor源码解读

补充知识

位运算符

  • Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
  • 位运算符作用在所有的位上,并且按位运算。假设A = 60,B = 13;它们的二进制格式表示将如下:
A = 0011 1100
B = 0000 1101
----------------------
A & B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A = 1100 0011
操作符 描述
& 如果相对应位都是1,则结果为1,否则为0
| 如果相对应位都是0,则结果为0,否则为1
^ 如果相对应位值相同,则结果为0,否则为1
~ 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。
<< 按位左移运算符。左操作数按位左移右操作数指定的位数。
>> 按位右移运算符。左操作数按位右移右操作数指定的位数。
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。

ThreadPoolExecutor

部分重要字段

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
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;
  • ctl: 对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段, 它包含两部分的信息: 线程池的运行状态 (runState)和线程池内有效线程的数量 (workerCount),这里可以看到,使用了Integer类型来保存,高3位保存runState,低29位保存workerCountCOUNT_BITS就是29CAPACITY就是1左移29位减1(29个1),这个常量表示workerCount的上限值,大约是5亿。

  • 后面五个int值代表着线程池的状态:

    • RUNNING: 能够接受新提交的任务,并且能够处理阻塞队列中的任务。
    • SHUTDOWN: 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。
      • 在线程池处于RUNNING状态时,调用shutdown()方法会使线程池进入到该状态。
      • finalize()方法在执行过程中也会调用shutdown()方法进入该状态。
    • STOP: 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于RUNNINGSHUTDOWN状态时,调用shutdownNow()方法会使线程池进入到该状态;
    • TIDYING: 如果所有的任务都已终止了,workerCount(有效线程数) 为0,线程池进入该状态后会调用terminated()方法进入TERMINATED状态。
    • TERMINATED:terminated()方法执行完后进入该状态,默认terminated()方法中什么也没有做。
  • 进入TERMINATED的条件如下:

    1. 线程池不是**RUNNING** 状态;
    2. 线程池状态不是 TIDYING 状态或 TERMINATED 状态;
    3. 如果线程池状态是 SHUTDOWN 并且workerQueue为空;
    4. workerCount为0;
    5. 设置 TIDYING 状态成功。
      在这里插入图片描述

ctl相关方法

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
  • runStateOf: 获取运行状态;
  • workerCountOf: 获取活动线程数;
  • ctlOf: 获取运行状态和活动线程数的值。

ThreadPoolExecutor构造方法

	/**
     * @param corePoolSize    核心线程数,也可以说是最小线程数
     * @param maximumPoolSize 最大线程数
     * @param keepAliveTime   空闲活跃时间
     * @param unit            时间单位
     * @param workQueue       工作队列
     * @param threadFactory   线程工厂
     * @param 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.corePoolSize = corePoolSize;
    	this.maximumPoolSize = maximumPoolSize;
    	this.workQueue = workQueue;
    	this.keepAliveTime = unit.toNanos(keepAliveTime);
    	this.threadFactory = threadFactory;
    	this.handler = handler;
    }

execute方法

  • ThreadPoolExecutor类中的execute()方法如下
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        // ctl记录着线程的运行状态和活动线程数
        int c = ctl.get();
        /**
         * workerCountOf(c)方法取出低29位的值,表示当前活动的线程数;
         * 如果当前活动线程数小于corePoolSize, 则新建一个线程放入线程池中;
         * 并把任务添加到该线程中。
         */
        if (workerCountOf(c) < corePoolSize) {
            /**
             * addWorker(Runnable firstTask, boolean core)
             * true:使用corePoolSize来判断,
             * false:则根据maximumPoolSize来判断
             */
            if (addWorker(command, true)) {
                 return;
            }
            // 如果添加失败,则重新获取ctl值
            c = ctl.get();
        }
        /**
         * offer(E e)方法返回的是boolean值,标识添加任务是否成功
         * 如果当前线程池是运行状态并且任务添加到队列中是成功的
         */
        if (isRunning(c) && workQueue.offer(command)) {
            // 重新获取ctl值
            int recheck = ctl.get();
            /**
             * 再次判断线程池的运行状态,如果不是运行状态,则需要将任务从队列中成功移除,
             * 这样才符合执行reject(E e)方法。
             * reject方法是执行饱和策略,对该任务进行处理
             */
            if (! isRunning(recheck) && remove(command))
                reject(command);
            /**
             * 这里判断有效的线程数是否为0,如果是的话执行addWork方法;
             * 第一个参数:null,表示在线程池中创建一个线程,但不去启动;
             * 第二个参数:false,将线程池的线程数上限设置为maximumPoolSize
             */
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        /**
         * 执行到这里有两种情况:
         * 1. 线程池不是RUNNING状态
         * 2. 线程池是RUNNING状态,但是workCount >= corePoolSize并且workQueue已满
         * 这时调用addWorker(command,false)方法,表示通过maximumPoolSize判断,
         * 如果返回值为false,则取反执行reject方法,执行饱和策略对该任务进行处理。
         */
        else if (!addWorker(command, false))
            reject(command);
    }
  • 在执行execute()方法时,如果线程池状态一直是RUNNING时,它的执行过程如下:
    1. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务;
    2. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中;
    3. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;
    4. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
  • 注意addWorker(null, false);,也就是创建一个线程,但并没有传入任务,因为任务已经被添加到workQueue中了,所以worker在执行的时候,会直接从workQueue中获取任务。所以,在workerCountOf(recheck) == 0时执行addWorker(null, false);也是为了保证线程池在RUNNING状态下必须要有一个线程来执行任务。

addWorker方法

/**
 * addWorker方法的主要工作是在线程中创建一个新的线程并执行,
 * firstTask参数用于指定新增的线程执行的第一个任务,
 * core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,
 * false表示新增线程时需要判断当前活动线程数是否少于maximumPoolSize
 */
private boolean addWorker(Runnable firstTask, boolean core) {
	retry:
    // 第一层死循环
	for (;;) {
	    int c = ctl.get();
        // 获取运行时状态
	    int rs = runStateOf(c);

	    /**
         * 这个if判断,如果rs >= SHUTDOWN,则表示此时不再接收新任务;
         * 后面三个判断,使用的是&&逻辑符,只要有一个条件false则返回false,取反为true;
         * 1. rs == SHUTDOWN, 这时表示关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中的任务
         * 2. firstTask为空
         * 3. 阻塞队列不为空
         */
	    if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
	        return false;
		// 内嵌的一个死循环
	    for (;;) {
            // 获取有效的线程数
	        int wc = workerCountOf(c);
            /**
             * 如果wc > CAPACITY或者wc > 线程池的上限线程数,则返回false;
             */
	        if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
	            return false;
            // 尝试去添加workerCountm,如果成功则跳出第一个for循环
	        if (compareAndIncrementWorkerCount(c))
	            break retry;
            // 如果尝试失败,则重新虎丘ctl的值。
	        c = ctl.get();  // Re-read ctl
            // 如果线程池的的当前状态 发生改变了,则返回到外层for循环,继续执行
	        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 {
        // 根据firstTask来创建Worker对象
	    w = new Worker(firstTask);
        // 每一个Worker对象都会创建一个线程
	    final Thread t = w.thread;
	    if (t != null) {
	        final ReentrantLock mainLock = this.mainLock;
	        mainLock.lock();
	        try {
	            // Recheck while holding lock.
	            // Back out on ThreadFactory failure or if
	            // shut down before lock acquired.
	            int rs = runStateOf(ctl.get());
			   // rs < SHUTDOWN表示RUNNING状态;
                // 如果rs是RUNNING状态或者rs是SHUTDOWN状态并且firstTask为null,向线程池中添加线程;
                // 因为SHUTDOWN状态不可以继续添加任务,但是可以继续执行workQueue中的任务
	            if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
	                if (t.isAlive()) // precheck that t is startable
	                    throw new IllegalThreadStateException();
                    // workers实质是一个HashSet
	                workers.add(w);
	                int s = workers.size();
                    // largestPoolSize记录线程池中出现过的最大线程数量
	                if (s > largestPoolSize)
	                    largestPoolSize = s;
                    // 用来标识是否start线程
	                workerAdded = true;
	            }
	        } finally {
	            mainLock.unlock();
	        }
	        if (workerAdded) {
                // 启动线程
	            t.start();
	            workerStarted = true;
	        }
	    }
	} finally {
	    if (! workerStarted)
	        addWorkerFailed(w);
	}
    // 返回的是线程开启是否成功的状态
	return workerStarted;
}

Worker类

  • Worker类是一个内部类,源码如下
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    
    private static final long serialVersionUID = 6138294804551838833L;

    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    // 用来保存传入的任务
    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
        // 将传入的任务赋值给firstTask
        this.firstTask = firstTask;
        // 通过ThreadFactory来创建线程,用来处理任务的线程
        this.thread = getThreadFactory().newThread(this);
    }

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

    // Lock methods
    //
    // 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) {
        // 根据state是否是0来判断
        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;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}
  • 这个类的注释翻译如下:
    • Worker主要维护运行任务的线程的中断控制状态,以及其他次要的记帐功能。
    • 该类巧用地继承AbstractQueuedSynchronizer,以简化获取和释放每个任务执行周围的锁。这可以防止中断,这些中断旨在唤醒等待任务的工作线程,而不是中断正在运行的任务。
    • 我们实现了一个简单的不可重入互斥锁,而不是使用ReentrantLock,因为我们不希望工作任务在调用诸如setCorePoolSize之类的池控制方法时能够重新获得锁。此外,为了在线程实际开始运行任务之前抑制中断,我们将锁状态初始化为负值,并在启动时清除它(在runWorker中)。
  • 由于实现的是不可重入的互斥锁,通过tryAcquire方法可以看出来,所以这里不使用ReentrantLock,因为它是可重入的:
    1. lock方法一旦获取了独占锁,表示当前线程正在执行任务中;
    2. 如果正在执行任务,则不应该中断线程;
    3. 如果该线程现在不是独占锁的状态,也就是空闲状态,说明它没有处理任务,这时可以对该线程进行中断;
    4. 线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;
    5. 如果使用ReentrantLock,它是可重入的,这样如果在任务中调用了如setCorePoolSize这类线程池控制的方法,会中断正在运行的线程。
  • 所以,Worker继承自AQS,用于判断线程是否空闲以及是否可以被中断。

runWorker方法

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // 获取第一个任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 允许中断
    w.unlock(); // allow interrupts
    // 设置一个标识,是否因为异常退出循环
    boolean completedAbruptly = true;
    try {
        // 如果task为空,则通过getTask来获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 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) ||
                (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted())
                wt.interrupt();
            try {
                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 {
        // 传入completedAbruptly
        processWorkerExit(w, completedAbruptly);
    }
}
  • 源码中的注释如下:
    • 如果线程池正在停止,那么要保证当前线程是中断状态;
    • 如果不是的话,则要保证当前线程不是中断状态。
  • runWorker方法执行流程如下:
    1. while循环不断地通过getTask()方法获取任务;
    2. getTask()方法从阻塞队列中取任务;
    3. 如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态;
    4. 调用task.run()执行任务;
    5. 如果tasknull则跳出循环,执行processWorkerExit()方法;
    6. runWorker方法执行完毕,也代表着Worker中的run方法执行完毕,销毁线程。

getTask方法

  • 该方法用来从阻塞队列中获取任务,源码如下:
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.
        /**
         * 1. 如果线程池状态rs >= SHUTDOWN,也就是非RUNNING状态,再作以下判断:
         * 2.1 rs >= STOP,线程池是否正在stop;
         * 2.2 阻塞队列是否为空。
         * 如果以上条件都满足,则执行decrementWorkerCunt()方法,即将workerCount减1并返回null;
         * 如果当前线程池状态的值是SHUTDOWN或以上时,不允许再向阻塞队列中添加任务。
         */
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);// 活跃的工作线程数
        
        // Are workers subject to culling?
        /**
         * timed变量用于判断是否需要进行超时控制。
         * allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
         * wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
         * 对于超过核心线程数量的这些线程,需要进行超时控制。
         */
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        /**
         * wc > maximumPoolSize的情况是因为可能在此方法执行阶段同时执行了setMaximumPoolSize方法;
         * timed && timedOut如果为true,表示当前操作需要进行超时控制,
         * 并且上次从阻塞队列中获取任务时发生了超时;
         * 接下来判断,如果有效线程数量大于1,或者阻塞队列是空的,那么尝试将workerCount减1;
         * 如果减1失败,则返回重试。如果wc == 1时,说明当前线程池中只有唯一一个线程。
         */
        if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        
        try {
            /**
             * 根据timed来判断,如果为true,则通过阻塞队列的poll方法进行超时控制,
             * 如果在keepAliveTime时间内没有获取到任务,则返回null;
             * 否则通过take方法,如果这时队列为空,则take方法会阻塞直到队列不为空。
             */
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
            if (r != null) return r;
            // 如果 r == null,说明已经超时,timedOut设置为true
            timedOut = true;
        } catch (InterruptedException retry) {
            // 如果获取任务时当前线程发生了中断,则设置timedOut为false并返回循环重试
            timedOut = false;
        }
    }
}
  • 注意这里的第二个if判断,目的是控制线程池的有效线程数量。
    • 在执行execute方法时,如果corePoolSize < currentPoolSize < maximumPoolSize时,并且workQueue已满时,则可以增加工作线程。但这时如果超时没有获取到任务,也就是timedOuttrue的情况,说明workQueue已经为空了,也就说明了当前线程池中不需要那么多线程来执行任务,可以把多余的线程销毁,保证线程数量在corePoolSize即可。
    • 线程销毁则是runWorker方法执行完之后,也就是Worker中的run方法执行完,由JVM自动回收。
  • 注意:getTask方法返回null时,在runWorker方法中会跳出while循环,然后会执行processWorkerExit方法

processWorkerExit方法

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    /**
     * 如果completedAbruptly值为true,则说明线程执行时出现了异常,需要将workerCount减1;
	 * 如果线程执行时没有出现异常,
	 * 说明在getTask()方法中已经已经对workerCount进行了减1操作,这里就不必再减了。  
     */
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 统计完成的任务数
        completedTaskCount += w.completedTasks;
        // 从workers中移除
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    
    // 根据线程池状态进行判断是否结束线程池
    tryTerminate();
    int c = ctl.get();
    /**
     * 当线程池是RUNNING或SHUTDOWN状态时,如果worker是异常结束,那么会直接addWorker;
     * 如果allowCoreThreadTimeOut=true,并且等待队列有任务,至少保留一个worker;
     * 如果allowCoreThreadTimeOut=false,workerCount不少于corePoolSize。
     */
    if (runStateLessThan(c, STOP)) {
        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);
    }
}
  • 至此,processWorkerExit执行完之后,工作线程被销毁,以上就是整个工作线程的生命周期,从execute方法开始,Worker使用ThreadFactory创建新的工作线程,runWorker通过getTask获取任务,然后执行任务,如果getTask返回null,进入processWorkerExit方法,整个线程结束,如图所示:

在这里插入图片描述

tryTerminate方法

  • tryTerminate方法根据线程池状态进行判断是否结束线程池,代码如下:
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        /**
         * 当前线程池的状态为以下几种情况时,直接返回
         * 1.RUNNING,因为还在运行中,不能停止
         * 2.TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;
         * 3.SHUTDOWN并且等待队列为空,这时要执行完workQueue中的Task。
         */
        if (isRunning(c) || runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        
        // 如果线程数量不为0,则中断一个空闲的工作线程,并返回
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
        
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 这里尝试设置状态为TIDYING,如果设置成功,则调用terminated方法
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 设置状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

shutdown方法

  • shutdown方法要将线程池切换到SHUTDOWN状态,并调用interruptIdleWorkers方法请求中断所有空闲的worker,最后调用tryTerminate尝试结束线程池。
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 安全策略判断
        checkShutdownAccess();
        // 切换状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        // 中断空闲线程
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    // 尝试结束线程池
    tryTerminate();
}

runWorker方法中,执行任务时对Worker对象w进行了lock操作,为什么要在执行任务的时候对每个工作线程都加锁呢?

  • getTask方法中,如果这时线程池的状态是SHUTDOWN并且workQueue为空,那么就应该返回null来结束这个工作线程,而使线程池进入SHUTDOWN状态需要调用shutdown方法;
  • shutdown方法会调用interruptIdleWorkers来中断空闲的线程,interruptIdleWorkers持有mainLock,会遍历workers来逐个判断工作线程是否空闲。但getTask方法中没有mainLock
  • getTask中,如果判断当前线程池状态是RUNNING,并且阻塞队列为空,那么会调用workQueue.take()进行阻塞;
  • 如果在判断当前线程池状态是RUNNING后,这时调用了shutdown方法把状态改为了SHUTDOWN,这时如果不进行中断,那么当前的工作线程在调用了workQueue.take()后会一直阻塞而不会被销毁,因为在SHUTDOWN状态下不允许再有新的任务添加到workQueue中,这样一来线程池永远都关闭不了了;
  • 由上可知,shutdown方法与getTask方法(从队列中获取任务时)存在竞态条件;
  • 解决这一问题就需要用到线程的中断,也就是为什么要用interruptIdleWorkers方法。在调用workQueue.take()时,如果发现当前线程在执行之前或者执行期间是中断状态,则会抛出InterruptedException,解除阻塞的状态;
  • 但是要中断工作线程,还要判断工作线程是否是空闲的,如果工作线程正在处理任务,就不应该发生中断;
  • 所以Worker继承自AQS,在工作线程处理任务时会进行lockinterruptIdleWorkers在进行中断时会使用tryLock来判断该工作线程是否正在处理任务,如果tryLock返回true,说明该工作线程当前未执行任务,这时才可被中断

interruptIdleWorkers方法

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

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        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();
    }
}
  • interruptIdleWorkers遍历workers中所有的工作线程,若线程没有被中断tryLock成功,就中断该线程。
  • 之所以需要持有mainLock。因为workersHashSet类型的,不能保证线程安全。

shutdownNow方法

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;
}
  • shutdownNowshowdown方法区别:
    1. 设置状态为**STOP**;
    2. 中断所有工作线程,无论是否空闲;
    3. 返回阻塞队列中没有被执行的任务。
  • shutdownNow方法执行完之后调用tryTerminate方法,目的就是使线程池的状态设置为TERMINATED

线程池的监控

线程池中的一些属性可以在监控线程池的时候使用:

  • getTaskCount:线程池已经执行的和未执行的任务总数;
  • getCompletedTaskCount:线程池已完成的任务数量,该值小于等于taskCount;
  • getLargestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize
  • getPoolSize:线程池当前的线程数量;
  • getActiveCount:当前线程池中正在执行任务的线程数量。

通过这些方法,可以对线程池进行监控,在ThreadPoolExecutor类中提供了几个空方法,如beforeExecute方法,afterExecute方法和terminated方法,可以扩展这些方法在执行前或执行后增加一些新的操作,例如统计线程池的执行任务的时间等,可以继承自ThreadPoolExecutor来进行扩展。

线程池execute方法执行流程大致如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lingboo111/article/details/88658333