ThreadPoolExecutor 参数详解

一、 ThreadPoolExecutor 数据成员

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

ctl 主要用于存储线程池的工作状态以及池中正在运行的线程数。显然要在一个整型变量存储两个数据,只能将其一分为二。其中高3bit用于存储线程池的状态,低位的29bit用于存储正在运行的线程数。

1.1、线程池的状态

线程池具有以下五种状态,当创建一个线程池时初始化状态为 RUNNING 。

线程池 的状态 说明
RUNNING 允许提交并处理任务
SHUTDOWN 不允许提交新的任务,但是会处理完已提交的任务
STOP 不允许提交新的任务,也不会处理阻塞队列中未执行的任务,并设置正在执行的线程的中断标志位
TIDYING 所有任务执行完毕,池中工作的线程数为0,等待执行terminated()勾子方法
TERMINATED terminated()勾子方法执行完毕

注意,这里说的是线程池的状态,而不是池中线程的状态。

调用线程池的 shutdown 方法,将线程池由 RUNNING(运行状态)转换为 SHUTDOWN状态。

调用线程池的 shutdownNow 方法,将线程池由 RUNNING 或 SHUTDOWN 状态转换为 STOP 状态。

SHUTDOWN 状态 和 STOP 状态 先会转变为 TIDYING 状态,最终都会变为 TERMINATED

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

ThreadPoolExecutor 同时提供上述三个方法用于池中的线程查看线程池的状态和计算正在运行的线程数。

1.2、ThreadPoolExecutor 的核心参数

private int largestPoolSize;
private final BlockingQueue<Runnable>workQueue;
private volatile long keepAliveTime;
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;

上述数据成员对线程池的性能也有很大的影响,我会将它们放到构造中讲解。

private final HashSet<Worker> workers= new HashSet<Worker>();
private long completedTaskCount;
private volatile boolean allowCoreThreadTimeOut;
private int largestPoolSize;

completedTaskCount 表示线程池已完成的任务数。
allowCoreThreadTimeeOut 表示是否允许核心线程在空闲状态下自行销毁。
largestPoolSize 表示线程池从创建到现在,池中线程的最大数量

1.3、workers

private final HashSet<Worker> workers = new HashSet<Worker>();

workers 是个HashSet容器,它存储的是Worker类的对象,Worker是线程池的内部类,它继承了Runnable接口,不严格的情况下,可以将一个Worker对象看成Thread对象,也就是工作的线程。shutdown和shutdownNow方法中会使用workers完成对所有线程的遍历。

1.4、mainLock

private final ReentrantLock mainLock =new ReentrantLock();
private final Condition termination = mainLock.newCondition();

mainLock 主要用于同步访问(或者说改变)线程池的状态以及线程池的各项参数,比如 completedTaskCount 和 workers 等。

awaitTermination 方法中,(mianLock的)termination是用于延时的条件队列。

二、 ThreadPoolExecutor 的构造函数

public  ThreadPoolExecutor(intcorePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler)

线程池的构造函数参数多达7个,现在我们一一来分析它们对线程池的影响。

  1. corePoolSize : 线程池中核心线程数的最大值
  2. maximumPoolSize : 线程池中能拥有最多线程数
  3. workQueue: 用于缓存任务的阻塞队列

2.1、corePoolSize、maximumPoolSize、workQueue 三者关系

我们现在通过向线程池添加新的任务来说明着三者之间的关系。

(1)如果没有空闲的线程执行该任务且当前运行的线程数少于 corePoolSize :,则添加新的线程执行该任务。

(2)如果没有空闲的线程执行该任务且当前的线程数等于 corePoolSize :,同时阻塞队列未满,则将任务入队列,而不添加新的线程。

(3)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数小于maximumPoolSize: ,则创建新的线程执行任务。

(4)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数等于maximumPoolSize: ,则根据构造函数中的 handler 指定的策略来拒绝新的任务。

注意,线程池并没有标记哪个线程是核心线程,哪个是非核心线程,线程池只关心核心线程的数量。

通俗解释, 如果把线程池比作一个单位的话, corePoolSize :就表示正式工,线程就可以表示一个员工。当我们向单位委派一项工作时,如果单位发现正式工还没招满,单位就会招个正式工来完成这项工作。随着我们向这个单位委派的工作增多,即使正式工全部满了,工作还是干不完,那么单位只能按照我们新委派的工作按先后顺序将它们找个地方搁置起来,这个地方就是 workQueue: ,等正式工完成了手上的工作,就到这里来取新的任务。如果不巧,年末了,各个部门都向这个单位委派任务,导致 workQueue:已经没有空位置放新的任务,于是单位决定招点临时工吧(临时工:又是我!)。临时工也不是想招多少就找多少,上级部门通过这个单位的 maximumPoolSize:确定了你这个单位的人数的最大值,换句话说最多招maximumPoolSize – corePoolSize:个临时工。当然,在线程池中,谁是正式工,谁是临时工是没有区别,完全同工同酬。

参考文章:
corePoolSize、maximumPoolSize、workQueue 三者关系的文章:
https://www.cnblogs.com/cdf-opensource-007/p/8769777.html

2.2、keepAliveTime、TimeUnit 存活时间和单位

参考keepAliveTime : 表示空闲线程的存活时间。
参考TimeUnit unit : 表示keepAliveTime的单位。

为了解释 keepAliveTime的作用,我们在上述情况下做一种假设。假设线程池这个单位已经招了些临时工,但新任务没有继续增加,所以随着每个员工忙完手头的工作,都来 workQueue领取新的任务(看看这个单位的员工多自觉啊)。随着各个员工齐心协力,任务越来越少,员工数没变,那么就必定有闲着没事干的员工。这样的话领导不乐意啦,但是又不能轻易fire没事干的员工,因为随时可能有新任务来,于是领导想了个办法,设定了 keepAliveTime,当空闲的员工在 keepAliveTime这段时间还没有找到事情干,就被辞退啦,毕竟地主家也没有余粮啊!当然辞退到 corePoolSize个员工时就不再辞退了,领导也不想当光杆司令啊!

2.3、workQueue 任务队列

workQueue 它决定了缓存任务的排队策略。对于不同的应用场景我们可能会采取不同的排队策略,这就需要不同类型的队列。这个队列需要一个实现了BlockingQueue接口的任务等待队列。

在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是: SynchronousQueueLinkedBlockingQueueArrayBlockingQueue

可参考:https://blog.csdn.net/xiaojin21cen/article/details/87363143

2.3.2、有限队列

  • SynchronousQueue :(一个不存储元素的阻塞队列) 一个 不存储元素的阻塞队列
    。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于
    阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool使用了这个队列。
  • ArrayBlockingQueue:(有界阻塞队列) 一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。
    新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

2.3.3、 无限队列

  • LinkedBlockingQueue:(链表结构的阻塞队列,尾部插入元素,头部取出元素) LinkedBlockingQueue是我们在ThreadPoolExecutor线程池中常用的等待队列。它可以指定容量也可以不指定容量。由于它具有“无限容量”的特性,所以我还是将它归入了无限队列的范畴(实际上任何无限容量的队列/栈都是有容量的,这个容量
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadPoolExecutor 是 Java 提供的用于创建线程池的类,它的构造函数有很多参数,下面是对这些参数的详细解释: 1. corePoolSize:线程池中核心线程的数量。当线程池中的线程数量小于 corePoolSize 时,新的任务会一直创建新的线程直到达到 corePoolSize 个线程。 2. maximumPoolSize:线程池中最大线程数。当线程池中的线程数量达到 corePoolSize 后,新的任务会被放入到等待队列中,等待被执行。如果等待队列已满,且线程池中的线程数量小于 maximumPoolSize,则会创建新的线程执行任务。 3. keepAliveTime:线程池中非核心线程的超时时间。当线程池中的线程数量大于 corePoolSize 时,多余的线程会被回收,但回收前会等待 keepAliveTime 时间,如果在这个时间内没有新的任务需要执行,则这个线程会被终止。 4. TimeUnit:超时时间的单位。 5. workQueue:用于缓存等待执行的任务的队列。ThreadPoolExecutor 提供了多种队列,如 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue 等。可以根据需求选择不同的队列。 6. threadFactory:用于创建新的线程。ThreadPoolExecutor 默认使用 Executors.defaultThreadFactory() 创建线程。如果需要自定义线程创建方式,可以实现 ThreadFactory 接口。 7. handler:线程池中的线程数量达到 maximumPoolSize,并且等待队列已满时,新的任务的处理策略。ThreadPoolExecutor 提供了 4 种策略: - AbortPolicy:直接抛出异常; - CallerRunsPolicy:不在新线程中执行任务,而是让调用 execute 方法的线程执行任务; - DiscardOldestPolicy:丢弃最老的任务,执行当前任务; - DiscardPolicy:直接丢弃任务。 这些参数可以根据实际需求进行调整,以达到最优的线程池效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值