详解 ThreadPoolExecutor 的参数含义及源码执行流程

前言

在阿里巴巴的开发者手册中针对线程池有如下说明:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样

的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明: Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool 和 SingleThreadPool :

允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。

2) CachedThreadPool 和 ScheduledThreadPool :

允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。

Executors.newFixedThreadPool();

Executors.newSingleThreadExecutor();

Executors.newCachedThreadPool();

// 等方法的底层都是通过 ThreadPoolExecutor 实现的

经典回答

ThreadPoolExecutor 的核心参数指的是他在构建时需要传递的参数

1. 构造方法

/**

* @param corePoolSize 表示线程池的常驻核心线程数

* @param maximumPoolSize 表示线程池在任务最多时, 最大可以创建的线程数

* @param keepAliveTime 表示线程的存活时间

* @param unit 表示存活时间的单位

* @param workQueue 表示线程池执行的任务队列

* @param threadFactory 表示线程的创建工厂(一般不指定, 默认使用 Executors.defaultThreadFactory())

* @param handler 表示指定线程池的拒绝策略

*/

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler) {

if (corePoolSize < 0 ||

// maximumPoolSize 必须大于0, 且必须大于 corePoolSize

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;

}

2. execute()

public void execute(Runnable command) {

if (command == null)

throw new NullPointerException();

int c = ctl.get();

// 当前工作的线程数小于核心线程数

if (workerCountOf(c) < corePoolSize) {

// 创建新的线程执行此任务

if (addWorker(command, true))

return;

c = ctl.get();

}

// 检查线程池是否处于运行状态, 如果是则把任务添加到队列

if (isRunning(c) && workQueue.offer(command)) {

int recheck = ctl.get();

// 再次检查线程池是否处于运行状态, 防止在第一次校验通过后线程池关闭

// 如果是非运行状态, 则将刚加入队列的任务移除

if (! isRunning(recheck) && remove(command))

reject(command);

// 如果线程池的线程数为 0 时 (当 corePoolSize 设置为 0 时会发生)

else if (workerCountOf(recheck) == 0)

addWorker(null, false); // 新建线程执行任务

}

// 核心线程都在忙且队列都已爆满, 尝试新启动一个线程执行失败

else if (!addWorker(command, false))

// 执行拒绝策略

reject(command);

}

3. addWorker()

/**

* @param firstTask 线程应首先运行的任务, 如果没有则可以设置为null

* @param core 判断是否可以创建线程的阀值(最大值)

* - 如果等于 true 则表示使用 corePoolSize 作为阀值

* - false 则表示使用 maximumPoolSize 作为阀值

*/

private boolean addWorker(Runnable firstTask, boolean core) {

retry:

for (;;) {

int c = ctl.get();

int rs = runStateOf(c);

// Check if queue empty only if necessary.

if (rs >= SHUTDOWN &&

! (rs == SHUTDOWN &&

firstTask == null &&

! workQueue.isEmpty()))

return false;

for (;;) {

int wc = workerCountOf(c);

if (wc >= CAPACITY ||

wc >= (core ? corePoolSize : maximumPoolSize))

return false;

if (compareAndIncrementWorkerCount(c))

break retry;

c = ctl.get(); // Re-read ctl

if (runStateOf(c) != rs)

continue retry;

// else CAS failed due to workerCount change; retry inner loop

}

}

boolean workerStarted = false;

boolean workerAdded = false;

Worker w = null;

try {

w = new Worker(firstTask);

final Thread t = w.thread;

if (t != null) {

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

// Recheck while holding lock.

// Back out on ThreadFactory failure or if

// shut down before lock acquired.

int rs = runStateOf(ctl.get());

if (rs < SHUTDOWN ||

(rs == SHUTDOWN && firstTask == null)) {

if (t.isAlive()) // precheck that t is startable

throw new IllegalThreadStateException();

workers.add(w);

int s = workers.size();

if (s > largestPoolSize)

largestPoolSize = s;

workerAdded = true;

}

} finally {

mainLock.unlock();

}

if (workerAdded) {

t.start();

workerStarted = true;

}

}

} finally {

if (! workerStarted)

addWorkerFailed(w);

}

return workerStarted;

}

线程池执行主要流程

相关面试题

ThreadPoolExecutor 的执行方法有几种? 他们有什么区别?

execute() 不能接收返回值, 属于 Executor 接口

submit() 可以接收返回值, 属于 ExecutorService 接口

什么是线程的拒绝策略?

当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略

拒绝策略的分类有哪些?

AbortPolicy: 终止策略, 线程池会抛出异常并终止执行, 它是默认的拒绝策略

CallerRunsPolicy: 把任务交给当前线程来执行

DiscardPolicy: 忽略此任务(最新的任务)

DiscardOldestPolicy: 忽略最早的任务(最先加入队列的任务)

如何自定义拒绝策略?

新建一个 RejectedExecutionHandler 对象, 重写 rejectedExecution 方法即可

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2), new RejectedExecutionHandler(){ // 添加自定义拒绝策略

@Override

public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

// 业务处理方法

}

});

ThreadPoolExecutor 能不能实现扩展? 如何实现扩展?

通过 beforeExecute 和 afterExecute 方法实现

/**

* 线程池扩展

*/

static class MyThreadPoolExecutor extends ThreadPoolExecutor {

/**

* @param t 线程

* @param r 任务

*/

@Override

protected void beforExecute(Thread t, Runnable r){

// 开始执行之前

}

/**

* @param r 任务

* @param t 抛出的异常

*/

@Override

protected void afterExecute(Runnable r, Throwable t){

// 开始执行之后

}

}

java 源码学习,Java源码剖析34讲学习笔记~4相关推荐

  1. Android工程师进阶34讲学习笔记

    最近发现一个技术提升的平台,很多课程对于技术提升都多有益处,很多是实际上的项目实战并对底层原理讲解透彻.前几个月已经学习完了<Android 工程师进阶 34 讲>,现在重学一遍并做些总结 ...

  2. Java在线网校学习平台源码分享

    项目描述: Java在线网校学习平台源码分享 运行环境: jdk8+tomcat8+mysql5.7+eclipse(IntelliJ IDEA)+maven3.X 项目技术(必填): spring+ ...

  3. java中batch基础_详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码) 发布时间:2020-09-08 00:28:40 来源:脚本之家 阅读:99 作者:achuo Spring batch 是一个开源的批处理框架. ...

  4. Math源码java_深入学习java源码之Math.sin()与 Math.sqrt()

    深入学习java源码之Math.sin()与 Math.sqrt() native关键字 凡是一种语言,都希望是纯.比如解决某一个方案都喜欢就单单这个语言来写即可.Java平台有个用户和本地C代码进行 ...

  5. java 优秀源码_想要快速进阶Java架构师?这份超强(长)学习计划单 请签收!...

    优秀工程师的成长之路就是一条不断打怪升级之路的"修仙之路"! 而Java程序员一向比别人更难,如果说大家都在修仙的话,java程序员简直神似"剑修",入行枯燥精 ...

  6. 基于Java毕业设计智友少儿编程学习平台源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计智友少儿编程学习平台源码+系统+mysql+lw文档+部署软件 基于Java毕业设计智友少儿编程学习平台源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S ...

  7. java计算机毕业设计智友少儿编程学习平台源码+mysql数据库+系统+部署+lw文档

    java计算机毕业设计智友少儿编程学习平台源码+mysql数据库+系统+部署+lw文档 java计算机毕业设计智友少儿编程学习平台源码+mysql数据库+系统+部署+lw文档 本源码技术栈: 项目架构 ...

  8. 转载:深入学习java源码之Callable.call()与Future.get()

    原始链接:https://blog.csdn.net/qq_35029061/article/details/86750369 深入学习java源码之Callable.call()与Future.ge ...

  9. 深入学习java源码之Math.max()与 Math.min()

    深入学习java源码之Math.max()与 Math.min() java基本数据类型及自动转型 8种基本数据类型及其所占空间大小: 一.byte,占用一个字节,取值范围为 -128-127,默认是 ...

最新文章

  1. 百度地图应用开发(一)
  2. CSS box-shadow 盒子阴影属性
  3. SSM之一(使用idea创建一个Spring+SpringMVC的项目)
  4. 主存和cache每一块相等_笔记:cpu中的cache(一)
  5. 一文详解:字节面试官必问的Mysql锁机制
  6. 行车记录仪稳定方案:TC358778XBG:RGB转MIPI DSI芯片,M-Star标配IC
  7. [转载] java中创建对象的方式
  8. 现实问题从数学化到离散化再到程序化
  9. php正则表达式 重复字符,php正则表达式匹配可能的重音字符
  10. 循序渐进之Maven(4) - 第一个SpringMVC项目
  11. CocoaPods 安装
  12. Axure8.0如何汉化?
  13. 关于小凡模拟器设置完后找不到所要配置文件的问题
  14. 直播系统源码,直播系统源码, 直播app系统源码
  15. IEEE 802各个协议
  16. Excel转换成VCF
  17. amap和amapcrap使用
  18. ff14服务器延迟滑步,ff14 5.0黑魔怎么玩_最终幻想14 5.0黑魔输出手法
  19. 用node-webkit接入steamAPI
  20. Windows 下TSI721驱动软件使用

热门文章

  1. linux批量创建和删除用户
  2. spring 中加载xml配置文件的方式
  3. java.net.UnknownHostException 未知的名称或服务
  4. WSO2 Api Manager 集成 Analytics
  5. JavaScript-数组相关作业
  6. Ubuntu上安装jdk出现的错误
  7. C语言的本质(37)——makefile之隐含规则和模式规则
  8. Centos环境下部署游戏服务器-iptables
  9. java的this()与super()用法
  10. 如何在 Quagga BGP 路由器中设置 IPv6 的 BGP 对等体和过滤