ThreadPoolExecutor参数以及源码介绍
1.前言
在阿里巴巴的《Java 开发手册》中是这样规定线程池的:
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor
的方式,这样的处理方式让写的读者更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回的线程池对象的弊端:
FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为
Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为
Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
其实当我们去看 Executors
的源码会发现,Executors.newFixedThreadPool()、Executors.newSingleThreadExecutor()
和 Executors.newCachedThreadPool() 等方法的底层都是通过 ThreadPoolExecutor
实现的
2.ThreadPoolExecutor的核心参数
ThreadPoolExecutor
的核心参数指的是它在构建时需要传递的参数,其构造方法如下所示:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> 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;
}
第 1 个参数:corePoolSize
表示线程池的常驻核心线程数。如果设置为
0,则表示在没有任何任务时,销毁线程池;如果大于
0,即使没有任务时也会保证线程池的线程数量等于此值。但需要注意,此值如果设置的比较小,则会频繁的创建和销毁线程(创建和销毁的原因会在本课时的下半部分讲到);如果设置的比较大,则会浪费系统资源,所以开发者需要根据自己的实际业务来调整此值。
第 2 个参数:maximumPoolSize
表示线程池在任务最多时,最大可以创建的线程数。官方规定此值必须大于
0,也必须大于等于
corePoolSize,此值只有在任务比较多,且不能存放在任务队列时,才会用到。
第 3 个参数:keepAliveTime
表示线程的存活时间,当线程池空闲时并且超过了此时间,多余的线程就会销毁,直到线程池中的线程数量销毁的等于
corePoolSize 为止,如果 maximumPoolSize 等于
corePoolSize,那么线程池在空闲的时候也不会销毁任何线程。
第 4 个参数:unit 表示存活时间的单位,它是配合 keepAliveTime 参数共同使用的。
第 5 个参数:workQueue
表示线程池执行的任务队列,当线程池的所有线程都在处理任务时,如果来了新任务就会缓存到此任务队列中排队等待执行。
第 6 个参数:threadFactory
表示线程的创建工厂,此参数一般用的比较少,我们通常在创建线程池时不指定此参数,它会使用默认的线程创建工厂的方法来创建线程,源代码如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
// Executors.defaultThreadFactory() 为默认的线程创建工厂
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
// 默认的线程创建工厂,需要实现 ThreadFactory 接口
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
接口来完成,这样就可以自定义线程的名称或线程执行的优先级了。
第 7 个参数:RejectedExecutionHandler
表示指定线程池的拒绝策略,当线程池的任务已经在缓存队列 workQueue
中存储满了之后,并且不能创建新的线程来执行此任务时,就会用到此拒绝策略,它属于一种限流保护的机制。
线程池的工作流程要从它的执行方法 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);
}
其中 addWorker(Runnable firstTask, boolean core) 方法的参数说明如下:
firstTask,线程应首先运行的任务,如果没有则可以设置为 null;
core,判断是否可以创建线程的阀值(最大值),如果等于 true 则表示使用
corePoolSize 作为阀值,false 则表示使用 maximumPoolSize 作为阀值。
ThreadPoolExecutor参数以及源码介绍的更多相关文章
- 线程池 ThreadPoolExecutor 类的源码解析
线程池 ThreadPoolExecutor 类的源码解析: 1:数据结构的分析: private final BlockingQueue<Runnable> workQueue; // ...
- 【高并发】通过ThreadPoolExecutor类的源码深度解析线程池执行任务的核心流程
核心逻辑概述 ThreadPoolExecutor是Java线程池中最核心的类之一,它能够保证线程池按照正常的业务逻辑执行任务,并通过原子方式更新线程池每个阶段的状态. ThreadPoolExecu ...
- GGTalk——C#开源即时通讯系统源码介绍系列(一)
坦白讲,我们公司其实没啥技术实力,之所以还能不断接到各种项目,全凭我们老板神通广大!要知道他每次的饭局上可都是些什么人物! 但是项目接下一大把,就凭咱哥儿几个的水平,想要独立自主.保质保量保期地一个个 ...
- 4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍
4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍 天涯明月笙 关注 2018.02.20 19:23* 字数 762 阅读 135 ...
- 1-开发共享版APP(源码介绍)-BUG修复
这一系列文章将介绍APP的源码,这一节作为所有BUG问题修复! https://www.cnblogs.com/yangfengwu/category/1512162.html //开发共享版A ...
- Java中常用的七个阻塞队列第二篇DelayQueue源码介绍
Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...
- ThreadPoolExecutor的execute源码分析
上一篇文章指出,ThreadPoolExecutor执行的步骤如下: 向线程池中添加任务,当任务数量少于corePoolSize时,会自动创建thead来处理这些任务: 当添加任务数大于corePoo ...
- 线程池 ThreadPoolExecutor 原理及源码笔记
前言 前面在学习 JUC 源码时,很多代码举例中都使用了线程池 ThreadPoolExecutor,并且在工作中也经常用到线程池,所以现在就一步一步看看,线程池的源码,了解其背后的核心原理. 公众号 ...
- Mysql系列六:(Mycat分片路由原理、Mycat常用分片规则及对应源码介绍)
一.Mycat分片路由原理 我们先来看下面的一个SQL在Mycat里面是如何执行的: , ); 有3个分片dn1,dn2,dn3, id=5000001这条数据在dn2上,id=10000001这条数 ...
随机推荐
- tk.mybatis selectByPrimaryKey无法正确识别主键
selectByPrimaryKey无法正确识别主键,查看日志,发现报如下错误: ==> Preparing: SELECT username,password,name,age,sex,bir ...
- JVM系列之:String,数组和集合类的内存占用大小
目录 简介 数组 String ArrayList HashMap HashSet LinkedList treeMap 总结 简介 之前的文章中,我们使用JOL工具简单的分析过String,数组和集 ...
- HashMap 中 get,put 方法源码实现
HashMap 是Map接口的一个实现类 1.线程 HashMap 非线程安全2.数据存储结构 hashMap采用Entity数组来存储 key-value对,每一个键值对组成一个Entity,键值对 ...
- 一些matlb会用到的函数
matlab这种软件功能很强大,但是都不是常常会用到,尤其是像在学生中.每次用的时候总会把一些基本的函数忘记,还的去网上查.我之前在使用的时候也总结过,可惜在一次数据丢失中全没了(︶︹︺). 从现在开 ...
- 超简单的jq图片上传
<label class="file_img" for="file_imgs"> <input class="file_imgs&q ...
- 一个简单的Android小实例分享,包含recycleView与recyclerView嵌套
先上图: 1.首页 2.第二页 3.第三页 项目目录: 代码不多,本人太懒,就不贴了 项目地址:
- Python重命名和删除文件
Python重命名和删除文件: rename(当前的文件名,新文件名): 将当前的文件名修改为新文件名 程序: # os.rename('旧名字',’新名字‘) import os os.rename ...
- PHP array_replace() 函数
实例 使用第二个数组($a2)的值替换第一个数组($a1)的值: <?php$a1=array("red","green");$a2=array(&quo ...
- C/C++编程笔记:C语言错误处理方法!如何更好地处理程序的错误?
C语言被忽视的一些小东西!C语言基础教程之错误处理. C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据.在发生错误时,大多数的 C 或 UNIX 函数调 ...
- EACCES: permission denied,mkdir … npm install 安装依赖问题解决
强哥最近在用hugeGraph图库做二次开发的时候,在打包的时遇到前端项目打包失败的问题: cwebp-bin@4.0.0 postinstall /home/hugegraph/my-hugegra ...