死磕 java线程系列之线程池深入解析——构造方法

(手机横屏看源码更方便)
注:java源码分析部分如无特殊说明均基于 java8 版本。
简介
ThreadPoolExecutor的构造方法是创建线程池的入口,虽然比较简单,但是信息量很大,由此也能引发一系列的问题,同样地,这也是面试中经常被问到的问题,下面彤哥只是列举了一部分关于ThreadPoolExecutor构造方法的问题,如果你都能回答上来,则可以不用看下面的分析了。
问题
(1)ThreadPoolExecutor有几个构造方法?
(2)ThreadPoolExecutor最长的构造方法有几个参数?
(3)keepAliveTime是做什么用的?
(7)核心线程会不会超时关闭?能不能超时关闭?
(4)ConcurrentLinkedQueue能不能作为任务队列的参数?
(5)默认的线程是怎么创建的?
(6)如何实现自己的线程工厂?
(7)拒绝策略有哪些?
(8)默认的拒绝策略是什么?
构造方法
好了,我们直接上代码。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
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;
}
ThreadPoolExecutor有四个构造方法,其中前三个最终都是调用最后一个,它有7个参数,分别为corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
corePoolSize
核心线程数。
当正在运行的线程数小于核心线程数时,来一个任务就创建一个核心线程;
当正在运行的线程数大于或等于核心线程数时,任务来了先不创建线程而是丢到任务队列中。
maximumPoolSize
最大线程数。
当任务队列满了时【本篇文章由公众号“彤哥读源码”原创】,来一个任务才创建一个非核心线程,但不能超过最大线程数。
keepAliveTime + unit
线程保持空闲时间及单位。
默认情况下,此两参数仅当正在运行的线程数大于核心线程数时才有效,即只针对非核心线程。
但是,如果allowCoreThreadTimeOut被设置成了true,针对核心线程也有效。
即当任务队列为空时,线程保持多久才会销毁,内部主要是通过阻塞队列带超时的poll(timeout, unit)方法实现的。
workQueue
任务队列。
当正在运行的线程数大于或等于核心线程数时,任务来了是先进入任务队列中的。
这个队列必须是阻塞队列,所以像ConcurrentLinkedQueue就不能作为参数,因为它虽然是并发安全的队列,但是它不是阻塞队列。
// ConcurrentLinkedQueue并没有实现BlockingQueue接口
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
implements Queue<E>, java.io.Serializable {
// ...【本篇文章由公众号“彤哥读源码”原创】
}
threadFactory
线程工厂。
默认使用的是Executors工具类中的DefaultThreadFactory类,这个类有个缺点,创建的线程的名称是自动生成的,无法自定义以区分不同的线程池,且它们都是非守护线程。
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
那怎么自定义一个线程工厂呢?
其实也很简单,自己实现一个ThreadFactory,然后把名称和是否是守护进程当作构造方法的参数传进来就可以了。
有兴趣的同学可以参考netty中的默认线程工厂或者google中的线程工厂。
io.netty.util.concurrent.DefaultThreadFactory
com.google.common.util.concurrent.ThreadFactoryBuilder
handler
拒绝策略。
拒绝策略表示当任务队列满了且线程数也达到最大了,这时候再新加任务,线程池已经无法承受了,这些新来的任务应该按什么逻辑来处理。
常用的拒绝策略有丢弃当前任务、丢弃最老的任务、抛出异常、调用者自己处理等待。
默认的拒绝策略是抛出异常,即线程池无法承载了,调用者再往里面添加任务会抛出异常。
默认的拒绝策略虽然比较简单粗暴,但是相对于丢弃任务策略明显要好很多,最起码调用者自己可以捕获这个异常再进行二次处理。
彩蛋
OK,ThreadPoolExecutor的构造方法这块我们今天进行了深入解析,关于这块,您还有什么问题呢?欢迎留言评论、私聊勾搭。
欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

死磕 java线程系列之线程池深入解析——构造方法的更多相关文章
- 死磕 java同步系列之CyclicBarrier源码解析——有图有真相
问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...
- 死磕 java同步系列之Phaser源码解析
问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...
- 死磕 java同步系列之StampedLock源码解析
问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...
- 死磕 java同步系列之Semaphore源码解析
问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...
- 死磕 java同步系列之ReentrantReadWriteLock源码解析
问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...
- 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...
- 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...
- 死磕 java同步系列之CountDownLatch源码解析
- 死磕 java同步系列之zookeeper分布式锁
问题 (1)zookeeper如何实现分布式锁? (2)zookeeper分布式锁有哪些优点? (3)zookeeper分布式锁有哪些缺点? 简介 zooKeeper是一个分布式的,开放源码的分布式应 ...
- 死磕 java同步系列之redis分布式锁进化史
问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:R ...
随机推荐
- vscode从听说到使用,vetur,prettier,htmljscssPrettify踩坑指南。
今天基于vue-cli(2.9.3)构建一个新的项目.我用的sublime,es6的代码格式要与公司格式兼容.采用了vue-cli自带的eslint后,有一些不统一的部分需要修改.先看看sublime ...
- 带着canvas去流浪系列之九 粒子动画【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 使用Python编写打字训练小程序【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/devcloud/article/detail ...
- 用Python调用华为云API接口发短信
[摘要] 用Python调用华为云API接口实现发短信,当然能给调用发短信接口前提条件是通过企业实名认证,而且有一个通过审核的短信签名,话不多说,showcode #!/usr/bin/python3 ...
- zz:NETCONF协议详解
随着SDN的大热,一个诞生了十年之久的协议焕发了第二春,它就是NETCONF协议.如果你在两年前去搜索NETCONF协议,基本得到的信息都是"这个协议是一个网管协议,主要目的是弥补SNMP协 ...
- 【已解决】解决IntelliJ IDEA控制台输出中文乱码问题
IntelliJ IDEA 真的是一款很方便的Java开发工具,但是关于中文乱码这个问题我不得不吐槽,这个编码也弄得这么麻烦干嘛,真想找idea开发者干架,我敢打包票我能在一分钟之内一拳飞过去让他跪下 ...
- 深入理解 Java 反射和动态代理
- 【RabbitMQ】使用学习
[RabbitMQ]使用学习 转载: ============================================================= =================== ...
- SoC的软件开发流程,主要包含一些Linux下的操作命令
该笔记主要记录SoC的软件开发流程,主要包含一些Linux下的操作命令 1. 编写design file .c .h 2. 编写makefile 可执行文件名,交叉编译环境,compile fl ...
- Git详细教程之创建本地仓库和连接Github仓库
首先要知道不只有代码,任何类型的文件都可以进行版本控制,版本控制很重要的一个特性就是你可以随时修改,比如git会记录你的任何操作,如果你想要回退到任何一个历史操作,你可以通过git轻松达到. 本地仓库 ...