聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源码分析

ThreadPoolExecutor是Executor执行框架最重要的一个实现类,提供了线程池管理和任务管理是两个最基本的能力。这篇通过分析ThreadPoolExecutor的源码来看看如何设计和实现一个基于生产者消费者模型的执行器。


生产者消费者模型

生产者消费者模型包含三个角色:生产者,工作队列,消费者。对于ThreadPoolExecutor来说,

1. 生产者是任务的提交者,是外部调用ThreadPoolExecutor的线程

2. 工作队列是一个阻塞队列的接口,具体的实现类可以有很多种。BlockingQueue<Runnable> workQueue;

3. 消费者是封装了线程的Worker类的集合。HashSet<Worker> workers = new HashSet<Worker>();



主要属性

明确了ThreadPoolExecutor的基本执行模型之后,来看下它的几个主要属性:

1. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));    一个32位的原子整形作为线程池的状态控制描述符。低29位作为工作者线程的数量。所以工作者线程最多有2^29 -1个。高3位来保持线程池的状态。ThreadPoolExecutor总共有5种状态:

     *   RUNNING:  可以接受新任务并执行
     *   SHUTDOWN: 不再接受新任务,但是仍然执行工作队列中的任务
     *   STOP:     不再接受新任务,不执行工作队列中的任务,并且中断正在执行的任务
     *   TIDYING:  所有任务被终止,工作线程的数量为0,会去执行terminated()钩子方法
     *   TERMINATED: terminated()执行结束


下面是一系列ctl这个变量定义和工具方法

 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;

    // 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; }

    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }

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

    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

    private boolean compareAndDecrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect - 1);
    }

    private void decrementWorkerCount() {
        do {} while (! compareAndDecrementWorkerCount(ctl.get()));
    }

2. private final BlockingQueue<Runnable> workQueue; 工作队列,采用了BlockingQueue阻塞队列的接口,具体实现类可以按照不同的策略来选择,比如有边界的ArrayBlockingQueue,无边界的LinkedBlockingQueue。

3. private final ReentrantLock mainLock = new ReentrantLock();  控制ThreadPoolExecutor的全局可重入锁,所有需要同步的操作都要被这个锁保护

4. private final Condition termination = mainLock.newCondition(); mainLock的条件队列,来进行wait()和notify()等条件操作

5. private final HashSet<Worker> workers = new HashSet<Worker>();  工作线程集合

6. private volatile ThreadFactory threadFactory; 创建线程的工厂,可以自定义线程创建的逻辑

7. private volatile RejectedExecutionHandler handler;  拒绝执行任务的处理器,可以自定义拒绝的策略

8. private volatile long keepAliveTime;   空闲线程的存活时间。可以根据这个存活时间来判断空闲线程是否等待超时,然后采取相应的线程回收操作

9. private volatile boolean allowCoreThreadTimeOut;  是否允许coreThread线程超时回收

10. private volatile int corePoolSize;  可存活的线程的最小值。如果设置了allowCoreThreadTimeOut, 那么corePoolSize的值可以为0。

11. private volatile int maximumPoolSize;  可存活的线程的最大值


工作线程创建和回收策略

ThreadPoolExecutor通过corePoolSize,maximumPoolSize, allowCoreThreadTimeOut,keepAliveTime等几个参数提供一个灵活的工作线程创建和回收的策略。

创建策略:

1. 当工作线程数量小于corePoolSize时,不管其他线程是否空闲,都创建新的工作线程来处理新加入的任务

2. 当工作线程数量大于corePoolSize,小于maximumPoolSize时,只有当工作队列满了,才会创建新的工作线程来处理新加入的任务。当工作队列有空余时,只把新任务加入队列

3. 把corePoolSize和maximumPoolSize 设置成相同的值时,线程池就是一个固定(fixed)工作线程数的线程。

回收策略:

1. keepAliveTime变量设置了空闲工作线程超时的时间,当工作线程数量超过了corePoolSize后,空闲的工作线程等待超过了keepAliveTime后,会被回收。后面会说怎么确定一个工作线程是否“空闲”。

2. 如果设置了allowCoreThreadTimeOut,那么core Thread也可以被回收,即当core thread也空闲时,也可以被回收,直到工作线程集合为0。


工作队列策略


工作队列BlockingQueue<Runnable> workQueue 是用来存放提交的任务的。它有4个基本的策略,并且根据不同的阻塞队列的实现类可以引入更多的工作队列的策略。

4个基本策略:

1. 当工作线程数量小于corePoolSize时,新提交的任务总是会由新创建的工作线程执行,不入队列

2. 当工作线程数量大于corePoolSize,如果工作队列没满,新提交的任务就入队列

3. 当工作线程数量大于corePoolSize,小于MaximumPoolSize时,如果工作队列满了,新提交的任务就交给新创建的工作线程,不入队列

4. 当工作线程数量大于MaximumPoolSize,并且工作队列满了,那么新提交的任务会被拒绝执行。具体看采用何种拒绝策略

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值