理解ThreadPoolExecutor线程池的corePoolSize、maximumPoolSize和poolSize

我们知道,受限于硬件、内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去执行提交的任务。然而对于多用户、高并发的应用来说,提交的任务数量非常巨大,一定会比允许的最大线程数多很多。为了解决这个问题,必须要引入排队机制,或者是在内存中,或者是在硬盘等容量很大的存储介质中。J.U.C提供的ThreadPoolExecutor只支持任务在内存中排队,通过BlockingQueue暂存还没有来得及执行的任务。

任务的管理是一件比较容易的事,复杂的是线程的管理,这会涉及线程数量、等待/唤醒、同步/锁、线程创建和死亡等问题。ThreadPoolExecutor与线程相关的几个成员变量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它们共同负责线程的创建和销毁。

corePoolSize

线程池的基本大小,即在没有任务需要执行的时候线程池的大小并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。

maximumPoolSize

线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。

poolSize

线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。

现在我们通过ThreadPoolExecutor.execute()方法,看一下这3个属性的关系,以及线程池如何处理新提交的任务。以下源码基于JDK1.6.0_37版本。

private boolean addIfUnderCorePoolSize(Runnable firstTask) {Thread t = null;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (poolSize < corePoolSize && runState == RUNNING)t = addThread(firstTask);} finally {mainLock.unlock();}if (t == null)return false;t.start();return true;}private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {Thread t = null;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (poolSize < maximumPoolSize && runState == RUNNING)t = addThread(firstTask);} finally {mainLock.unlock();}if (t == null)return false;t.start();return true;}

新提交一个任务时的处理流程很明显:

1、如果当前线程池的线程数还没有达到基本大小(poolSize < corePoolSize),无论是否有空闲的线程新增一个线程处理新提交的任务;

2、如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列未满时,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);

3、如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列满时

3.1、当前poolSize<maximumPoolSize,那么就新增线程来处理任务;

3.2、当前poolSize=maximumPoolSize,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务,取决于线程池的饱和策略RejectedExecutionHandler。

Queuing
Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
    1、corePoolSize:核心线程数* 核心线程会一直存活,及时没有任务需要执行* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭2、queueCapacity:任务队列容量(阻塞队列)* 当核心线程数达到最大时,新任务会放在队列中排队等待执行3、maxPoolSize:最大线程数* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常4、 keepAliveTime:线程空闲时间* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize* 如果allowCoreThreadTimeout=true,则会直到线程数量=05、allowCoreThreadTimeout:允许核心线程超时6、rejectedExecutionHandler:任务拒绝处理器* 两种情况会拒绝处理任务:- 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务- 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务* 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常* ThreadPoolExecutor类有几个内部实现类来处理这类情况:- AbortPolicy 丢弃任务,抛运行时异常- CallerRunsPolicy 执行任务- DiscardPolicy 忽视,什么都不会发生- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务* 实现RejectedExecutionHandler接口,可自定义处理器

接下来我们看下allowCoreThreadTimeOut和keepAliveTime属性的含义。

在压力很大的情况下,线程池中的所有线程都在处理新提交的任务或者是在排队的任务,这个时候线程池处在忙碌状态。如果压力很小,那么可能很多线程池都处在空闲状态,这个时候为了节省系统资源,回收这些没有用的空闲线程,就必须提供一些超时机制,这也是线程池大小调节策略的一部分。通过corePoolSize和maximumPoolSize,控制如何新增线程;通过allowCoreThreadTimeOut和keepAliveTime,控制如何销毁线程。

allowCoreThreadTimeOut:

该属性用来控制是否允许核心线程超时退出。默认值为:falseIf false,core threads stay alive even when idle.If true, core threads use keepAliveTime to time out waiting for work。如果线程池的大小已经达到了corePoolSize,不管有没有任务需要执行,线程池都会保证这些核心线程处于存活状态。可以知道:该属性只是用来控制核心线程的。

keepAliveTime:

如果一个线程处在空闲状态的时间超过了该属性值,就会因为超时而退出。举个例子,如果线程池的核心大小corePoolSize=5,而当前大小poolSize =8,那么超出核心大小的线程,会按照keepAliveTime的值判断是否会超时退出。如果线程池的核心大小corePoolSize=5,而当前大小poolSize =5,那么线程池中所有线程都是核心线程,这个时候线程是否会退出,取决于allowCoreThreadTimeOut。

    Runnable getTask() {for (;;) {try {int state = runState;if (state > SHUTDOWN)return null;Runnable r;if (state == SHUTDOWN) // Help drain queuer = workQueue.poll();else if (poolSize > corePoolSize || allowCoreThreadTimeOut)r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);elser = workQueue.take();if (r != null)return r;if (workerCanExit()) {if (runState >= SHUTDOWN) // Wake up othersinterruptIdleWorkers();return null;}// Else retry} catch (InterruptedException ie) {// On interruption, re-check runState}}}

(poolSize > corePoolSize || allowCoreThreadTimeOut)这个条件,就是用来判断是否允许当前线程退出。workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);就是借助阻塞队列,让空闲线程等待keepAliveTime时间之后,恢复执行。这样空闲线程会由于超时而退出。

ThreadPoolExecutor线程池核心参数详解相关推荐

  1. Java线程池七大参数详解和配置

    目录 一.corePoolSize核心线程数 二.maximunPoolSize最大线程数 三.keepAliveTime空闲线程存活时间 四.unit空闲线程存活时间的单位 五.workQueue线 ...

  2. 并发编程五:java并发线程池底层原理详解和源码分析

    文章目录 java并发线程池底层原理详解和源码分析 线程和线程池性能对比 Executors创建的三种线程池分析 自定义线程池分析 线程池源码分析 继承关系 ThreadPoolExecutor源码分 ...

  3. 线程池invokeAll方法详解

    线程池invokeAll方法详解 问题起源与抽象 问题排查与猜测 猜测一:invokeAll 在异步执行后会不会同步等待线程执行完毕获取最终结果 猜测二:队列里面可能存在第一次调用 invokeAll ...

  4. pthread_create函数的详细讲解(包括向线程函数传递参数详解)

    pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int pthread_create(pthread_t*restrict ...

  5. 线程与线程池(一条龙详解)

    一:前言 一个问题引出的学习笔记 并发类库提供的线程池实现有哪些? 其实Executors已经为我们封装好了 4 种常见的功能线程池,如下: 定长线程池(FixedThreadPool) 定时线程池( ...

  6. 第9课:jvm的gc时候核心参数详解:-XX:NewRatio、-XX:SurvivorRatio、-XX:NewSize、-XX:MaxNewSize

    内容: 1.-XX:NewRatio     2.-XX:SurvivorRatio     3.-XX:NewSize和-XX:MaxNewSize 一.JVM内存结构图 二.参数详解 1.-xx: ...

  7. 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )

    文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...

  8. 【多线程】线程池拒绝策略详解与自定义拒绝策略

    线程池的拒绝策略 ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略 CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务 ...

  9. ThreadPoolExecutor线程池及参数介绍

    线程池类图如下: 使用Executors工具类创建的线程池,都是创建的ThreadPoolExecutor对象.这个对象的核心参数有7个: 1.corePoolSize 核心线程数,即便线程空闲也会一 ...

最新文章

  1. 最新Spring整合MyBatis详解教程
  2. 解决phpQuery(DOMDocument)解析部分HTML会产生乱码的问题
  3. python设置ini文件中的值_PyCharm设置python文件模板,自动读取文件信息。
  4. 《Android 应用测试指南》——第2章,第2.4节包浏览器
  5. 关于BEA-000402和BEA-000438(没有进程来读取写入管道的数据)
  6. dialog窗口编程的入门使用
  7. WPS2012交叉引用技巧,word比wps这点强更新參考文献
  8. LintCode Python 简单级题目 112.删除链表中的重复元素
  9. 2ask信号调制与解调 matlab,2ASK2ASK调制与解调系统的MATLAB实现及性能分析
  10. js获取当前URL中的参数
  11. 集合的相关概念(开闭、有界无界、内点边界点等)
  12. windows快速创建文本文档的几个方法快捷键和
  13. 2019年最新手机CPU处理器性能排行天梯图
  14. java 合并mp3
  15. 水平线标记的用法和属性
  16. 织梦留言板模板 .php,DEDECMS 留言薄模块的使用方法
  17. matlab 双y轴 三y轴
  18. 'config.h' file not found 的解决方法以及可能遇到的问题
  19. 布局布线流程的10大步骤
  20. mysql ddl之增加字段

热门文章

  1. 关闭wamp警告php语句,php,_php的wamp环境搭建问题,php - phpStudy
  2. 【Linux】循序渐进学运维-服务篇-nginx入门
  3. 前端------ canvas 绘制折线图详解
  4. 生鲜水果批发APP开发功能模块
  5. java培训后好找工作吗
  6. 【深度学习】AE与VAE
  7. 三维建模----相机参数
  8. Unity3D导航 爬楼梯、跳楼、分路线前进
  9. Spring Boot从入门到精通(超详细)
  10. 软件工程期末复习(一小时通过考试,全是重点)