ThreadPool 框架分析

一. 构造参数和属性含义

corePoolSize: 池子中保持的线程数量, 即使空闲, 也不会销毁
maximumPoolSize: 池子中最大的线程数
keepAliveTime: 当池子中线程数量大于 corePoolSize 时, 哪些空闲的线程多久会 timeout 被销毁
workQueue: 待执行的任务池
RejectedExecutionHandler 拒绝策略: 默认 AbortPolicy, 直接抛出异常

// 全局的 worker 列表
private final HashSet<Worker> workers = new HashSet<Worker>();  
// 控制线程池全局动作的一把主锁. 将线程池 shutdown , 增加 worker , intrupt worker这些操作同步, 避免操作混乱
private final ReentrantLock mainLock = new ReentrantLock();
// 构造方法中传入的工作队列
private final BlockingQueue<Runnable> workQueue;
二. ctl 变量

ctl 一个变量同时记录线程池状态和 RUNNING TASK 的数量.
如下可见线程池有5个状态 (RUNNING … TERMINATED), 所以需要3个比特位记录状态. jdk选择 int 值的高3位记录线程池状态, 低29位记录 RUNNING TASK 的数量. 所以:
* COUNT_BITS = 29
* CAPACITY 高3位全0, 低29位全1

// 记录 runningState 和 workCount 的变量. 初始化为 RUNNING 态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

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

// 高3位记录的 RUNNING STATE
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;

// 取高3位
private static int runStateOf(int c)     { return c & ~CAPACITY; }

// 取低29位
private static int workerCountOf(int c)  { return c & CAPACITY; }

// ctl = "或"操作起到高3位记录 running state, 低29位记录 worker count
private static int ctlOf(int rs, int wc) { return rs | wc; }   
三. 线程池中创建线程的方法

线程池中的创建线程这个动作, 由 Executors 的静态内部类 DefaultThreadFactory 创建的

static class DefaultThreadFactory implements ThreadFactory {
	public Thread newThread(Runnable r) {
	    Thread t = new Thread(group, r,
	                          namePrefix + threadNumber.getAndIncrement(),
	                          0);
	    if (t.isDaemon())
	        t.setDaemon(false);
	    if (t.getPriority() != Thread.NORM_PRIORITY)
	        t.setPriority(Thread.NORM_PRIORITY);
	    return t;
	}
}
四. execute() 的执行分三步

线程池执行任务的三个分支:

public void execute(Runnable command) {
    int c = ctl.get();
    // 分支1 - 新创建线程: 
    //     如果池子中有少于 corePoolSize 的线程在执行, start 一个新线程, 这个任务当做线程的第一个任务. 
    //     addWorker() 内部会原子性的检查 runState 和 workerCount 2个变量, 不该创建新线程时该调用返回 false  
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 分支2 - worker 数量已经达到 corePoolSize), 将任务放入全局队列
    //        后面会判断, 如果是在线程池 shutDown 后向队列添加 runnable, 会执行拒绝策略
    if (isRunning(c) && workQueue.offer(command)) {
    	// 如果一个 task 可以成功的加入队列, 仍需要 double check 是否需要增加一个线程 
        // (因为可能在上次检查后, 有线程死掉了或线程池已经被 shutdown) 
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 分支3: 如果 task 不能成功加入队列, 则可能是线程池已经 shutdown, 或者已经饱和该执行决绝策略了  
    else if (!addWorker(command, false))
        reject(command);
}
  1. addWorker() 内部还会校验线程数量, 是否需要新建
private boolean addWorker(Runnable firstTask, boolean core) {
	retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);   // running state
 
        int c = ctl.get();
        int rs = runStateOf(c);

        // (1) 不加锁的情况下, 校验线程池状态的代码.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;  

        for (;;) {
            int wc = workerCountOf(c);  // worker 数量
            if (wc >= CAPACITY ||       // worker 数量太大, 低29位要溢出
                wc >= (core ? corePoolSize : maximumPoolSize))   // core: true/false, 决定 wc 是和 coreSize 还是和 maxSize 比较
                return false;
            // compareAndIncrementWorkerCount(c): CAS递增 ctl 变量, 方法内执行 ctl.compareAndSet(expect, expect + 1);
            if (compareAndIncrementWorkerCount(c))
                break retry;  // CAS 递增成功跳出最外层 retry 循环
            c = ctl.get();    // 重读 ctl
            if (runStateOf(c) != rs)   // 重读 ctl 后发现线程池状态改变, 要从判断状态开始重新循环
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }

		// (2) 开始在锁下执行创建线程 (Worker)
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        	// Worker 内部2个属性: thread 和 firstTask 
        	// new Worker() 会从 DefaultThreadFactory 创建新线程
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;   // 线程池主锁
                mainLock.lock();
                try {
                    // 在加锁的情况下再次检查, 防止锁获取前线程池 shutdown
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                        	// 新创建的线程还没使用不能是 alive 状态, 如果是, 
                    	    // 证明 ThreadFactory 中返回的线程正在使用, 是无效的
                            throw new IllegalThreadStateException();
                        workers.add(w);   // hashSet 中加入 worker
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true; 
                    }
                } finally {
                    mainLock.unlock();  // 新建成功后就释放锁
                }
                if (workerAdded) {
                    t.start();  // 执行新创建的线程中的 Runnable
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
            	// 主要是从全局的 works 中删除新创建的 worker, 并更改一些状态
            	// 修改 ctl, 减少1个 workerCount
                addWorkerFailed(w); 
        }
        return workerStarted;
    }
}
  1. 削减 workerCount
    死循环下 CAS 修改 worker 数量
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
  1. 操作的锁粒度, 由不加锁, 逐步到加锁的递进完成
    (1) 状态检查, 线程数量比较现在不加锁的状态下进行
    (2) 当初步判断符合某一个分支后, 先用 CAS 修改 ctl, 像低29位的 worker 数量等. 如果第一步检查失效, CAS 会操作失败
    (3) 真正执行增删节点的操作, 在加 mainLock 的情况下进行. 这把锁内部还会重新检查状态, 防止前两部检查遗漏
五. Worker 类的分析
// 继承 AQS 为了自动包含锁, 和被 interupt 的作用
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{

        final Thread thread; 
        Runnable firstTask;    // Initial task to run.  Possibly null
        volatile long completedTasks;  // Per-thread task counter

        /** 构造方法 */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);  // thread 属性也持有 Worker Runnable
        }

        /** 执行 thread.start() 会调用该 Worker.run() 方法  */
        public void run() {
            runWorker(this);  // 见下面分析
        }

Worker 和 内部 thread 是互相持有对方为属性的关系:

  • (1) worker 持有 thread 作为属性
  • (2) thread 持有 worker 作为属性. thread.start() 会执行内部 Runnable 的 run, 就是 Worker 对象的 run().
    Worker 对象一旦在线程的 addWorker() 方法中创建, 会被立刻开启内部的线程, 从而调用 worker#run(), 即内部的 runWorker
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts 的地方. 也是 worker 继承 AQS 的原因
    boolean completedAbruptly = true;
    try {
    	// getTask(): 从队列取任务. 
        while (task != null || (task = getTask()) != null) {
            w.lock();  // worker 正在被访问, 不允许其它方法执行了
            // 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())  // 响应 interrupt
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                	// 提到线程池的 runbale 对象的 run(), 
                	// 如果正常执行, 会跳回 while, 继续从全局队列中取任务
                    task.run(); 
                } 
                // 底下这些报错, 都会 throw, 导致内部执行线程结束
                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 {
    	// 任务执行出错会跳到这里. 
    	// 如果不足 corePoolSize, 会创建新的 worker, 代替失败的 worker 
        processWorkerExit(w, completedAbruptly);
    }
}
什么时候将非核心线程删除,让线程个数降低到 corePoolSize

首先明确,只有在有界队列已满后,线程池才会创建新的非核心线程,让线程数量超过 corePoolSize,小于等于maxPoolSize。该新建的非核心线程线程的runWorker()启动后立刻拿任务导致有界队列有空闲位置,从而可以提交新任务。如果线程池设置了超时时间,非核心线程在空闲一段时间后会自行销毁,这是怎么做到的呢?
答案:上文看到,runWorker() 中调用 getTask() 获取任务,

  • 如果设置了超时时间 getTask() 会调用带超时时间的 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 方法从阻塞队列中获取任务,超时返回 null
  • runWorker() 对于 null 任务会执行 processWorkerExit() 将 worker 对象从列表中删除
// ThreadPoolExecutor.java

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 如果触发了 shutown, getTask() 返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        // 判断 worker 是否要被选择宰杀(客户端选择可以宰杀核心线程,或线程池中的线程数量大于核心线程数)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        /** [关键处]:判断超时后,本方法返回 null, 
         *  runWorker 会在调用处执行 processWorkerExit() 将 worker 从 worker列表中删除 
         */ 
        if ((wc > maximumPoolSize   // (1) worker 数量大于 maxPoolSize
            || (timed && timedOut)) // (2) 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;
            // 如果设置了超时时间,阻塞队列因为超时而返回 null,timedOut 设为空
            // 下次循环会进入
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

// Worker 退出方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        // (1) 将此 worker 从worker列表中删除
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

		// (2) 如果 worker 数量不足 corePoolSize, 还会增加 Worker
        int c = ctl.get();
        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);
        }
    }
六. 线程池的 shutdown 和 interrupt
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值