ThreadPoolExecutor

线程池的实现原理:

调用方不断地向线程池中提交任务;线程池中有一组线程,不断地从队列中取任务,这是一个典型的生产者——消费者模型。具体过程如图所示:
ThreadPoolExecutor_第1张图片

基于线程池的实现原理,看一下ThreadPoolExecutor的核心数据结构

public class ThreadPoolExecutor extends AbstractExecutorService {
	...
	//状态变量
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	//存放任务的阻塞队列
	private final BlockingQueue<Runnable> workQueue;
	//对线程内部各种变量进行互斥访问控制
	private final ReentrantLock mainLock = new ReentrantLock();
	//线程集合
	private final HashSet<Worker> workers = new HashSet<Worker>();
	}

每一个线程是一个Worker对象。Worker是ThreadPoolExecutor的内部类,核心数据结构如下:

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
    	...

        //Worker封装的线程
        final Thread thread;
     
        //Worker接收到的第1个任务
        Runnable firstTask;
       
        //Worker执行完毕的任务个数
        volatile long completedTasks;
    }

由定义会发现,Worker继承于AQS,也就是说Worker本身就是一把锁。这把锁有什么用处呢?在分析线程池的关闭、线程执行任务的过程中可以了解到。

核心配置参数解释

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

(1)corePoolSize:核心工作线程数,在线程池中始终维护的线程个数。
(2)maxPoolSize:最大工作线程数,在corePoolSize已满、队列也满的情况下,扩充线程至此值。(代表当前线程池中,一共可以有多少个工作线程)
(3)keepAliveTime:空间线程存活时间即非核心工作线程在阻塞队列位置等待新任务的时间
(4)TimeUnit:非核心工作线程在阻塞队列位置等待时间的单位
(5)blockingQueue:线程池所用队列类型
(6)threadFactory:线程创建工厂,可以自定义,也有一个默认的
(7)RejectedExecutionHandler:corePoolSize已满,队列已满,maxPoolSize已满,最后的拒绝策略。
注意:线程池构建时不会初始化核心线程,所以只有执行任务时才启动线程,因此是线程池中的线程是懒加载的

下面来看看者6个配置参数在任务的提交过程中是怎么运行的。在每次往线程池中提交任务的时候,有如下的处理流程:
step1:判断当前线程数是否大于或等于corePoolSize。如果小于,则新建线程执行;如果大于,则进入step2;
step2:判断队列是否已满。如未满,则放入;如已满,则进入step3;
step3:判断当前线程数是否大于或等于maxPoolSize。如果小于,则新建线程执行;如果大于则进入step4。
step4:根据拒绝策略,拒绝任务。
  总结:首先判断corePoolSize,其次判断blockingQueue是否已满,接着判断maxPoolSize,最后使用拒绝策略。
  显然,基于这种流程,如果队列是无界的,将永远没有机会走到step3,也即maxPoolSize没有使用,也一定不会走到step4.

相关问题

线程池是如何区分核心线程和非核心线程的?

  线程池只有coresize和maximumsize,在数量上进行的逻辑处理,并没有在线程个体上做区分。
  当新建了线程,线程首先执行任务;执行完成之后会从workQueue队列中取任务。队列可能为空,因此线程取任务可能会阻塞。在从队列获取任务前,线程池会先进行判断,当线程数量>coreSize时,说明可以消减线程了,就会给该线程设置从队列取任务的最长阻塞时间(keepAliveTime),超时返回null,即表示该线程空闲了keepAliveTime时间,并且线程池数大于corePoolSize核心线程数,就会将该线程销毁。
  所以有个容易混淆的地方就是,当线程池中的线程超过了和核心线程数时,会将多出来的线程销毁,很多人误以为只会把“非核心线程”销毁掉,现在我们知道了,这两者之间并没有本质上的区别,所以,销毁的时候是随机的,可能是“核心线程”也可能是“非核心线程”。

你可能感兴趣的:(JAVA高并发,java,开发语言)