jdk1.8 ThreadPoolExecutor

ThreadPoolExecutor实际就是java的线程池,开发常用的Executors.newxxxxx()来创建不同类型和作用的线程池,其底部实际都是ThreadPoolExecutor。

1.创建 构造方法
以最多参数的为例

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;
}
corePoolSize:核心的线程数
maximumPoolSize:最大线程数
keepAliveTime:当现有线程数大于核心线程数时,没有执行任务的线程存活时间 CachedThreadPool弹性线程池有用60s,其它是0表示不回收
unit:存活时间单位
workQueue:阻塞的工作队列存放需要执行的任务
threadFactory:创建线程的工厂,实际就是给每个线程一个名,常见的pool-1-thread名就是它完成的
handler:线程池饱和策略,当工作队列满了,线程池创建的线程满了,就是忙不过来的时候线程池的处理方案 workQueue:阻塞队列实现主要有5种
1、ArrayBlockingQueue:数组结构的有界阻塞队列,按FIFO排序任务;
2、LinkedBlockingQuene:链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
固定线程池、单线程池
3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
弹性线程池
4、priorityBlockingQuene:具有优先级的无界阻塞队列;
5、DelayedWorkQueue:有定时性子的阻塞队列
定时线程池 handler:线程池饱和策略,主要有4种
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;

2.线程池执行任务execute方法
常用的invokeAll、invokeAny、submit是上层ExecutorService又封装的执行策略,底层只有execute

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}

ctl:需要详细学习,顺便都看了
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//初始高3位1后面全0,从后面代码可以知道,ctl实际是用高3位表示线程池状态,后面29位表示当前线程数
private static final int COUNT_BITS = Integer.SIZE - 3;//32-3=29
private static final int CAPACITY = (1 << COUNT_BITS) - 1;

//明显这5个代表当前线程池状态,实际使用高3位来表示
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;//-1左移位29结果11100000000000000000000000000000 高3位是1 接受新任务并且处理已经进入阻塞队列的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;//000 不接受新任务,但是处理已经进入阻塞队列的任务
private static final int STOP = 1 << COUNT_BITS;//001 不接受新任务,不处理已经进入阻塞队列的任务并且中断正在运行的任务
private static final int TIDYING = 2 << COUNT_BITS;//010 所有的任务都已经终止,workerCount为0, 线程转化为TIDYING状态并且调用terminated钩子函数
private static final int TERMINATED = 3 << COUNT_BITS;//011 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; }//安位或

一句一句:
int c = ctl.get(); 通过上面知道c有两个作用,线程池状态,线程数

if (workerCountOf(c) < corePoolSize) { workerCountOf(c)就不详细看了,作用是根据低29位获取当前线程数,判断小于核心线程数corePoolSize

if (addWorker(command, true)) 说明是小于核心线程数的这里就创建新线程执行该任务,创建成功那就没后面事了

c = ctl.get(); 这里说明创建新线程失败了,线程池大多是并发场景,别的线程先一步创建就可能导致超过核心线程数,这里再次获取线程池 ctl,因为可以肯定是有变化的

if (isRunning(c) && workQueue.offer(command)) { 能到这说明线程池的线程数>=corePoolSize,或者创建新线程失败也是超过核心线程数的,不管哪种情况都表明任务就需要等待了,
判断线程池是否RUNNING执行状态,是继续把当前任务添加到工作队列中入队

int recheck = ctl.get(); 再次检查,前面的检查执行状态和入队后这里的线程状态并发下是很可能有改变的

if (! isRunning(recheck) && remove(command)) 这里注意外层if检查线程池是执行状态的且入队成功的,再次检查如果变更了不是执行中,说明线程池关了,已经不再执行任务了,
当前任务从工作队列移除,执行饱和策略reject

else if (workerCountOf(recheck) == 0) 这里说明还是执行中,特别注意从工作队列中移除是不会失败的因为之前是保证入队成功了的。检查当前线程数没有线程就创建一个线程,
注意只是创建线程不是直接执行该任务,后续会取队列执行

else if (!addWorker(command, false)) 说明线程池非执行状态或提交任务失败,则创建新线程执行任务,实际是尝试提高线程数到最大线程数,addWorker中会判断线程池状态和线程数的,
失败就饱和策略

3.addworker创建新线程执行任务

private boolean addWorker(Runnable firstTask, boolean core) {
retry: //标签
for (;;) { //死循环 jdk多线程常干的事,然后CAS
int c = ctl.get(); //获取线程状态
int rs = runStateOf(c);//获取高3位即线程池状态 // Check if queue empty only if necessary.
if (rs >= SHUTDOWN && //线程池非运行状态
! (rs == SHUTDOWN && // 状态为SHUTDOWN
firstTask == null && // 传递的任务为null,对于execute方法线程池满编入队之后 会在这里过滤掉执行后面创建新线程去执行队列
! workQueue.isEmpty())) // worker队列不为空 这里也说明SHUTDOWN还会继续处理队列中的任务
return false; for (;;) {
int wc = workerCountOf(c); //当前线程数量
if (wc >= CAPACITY || //线程数大于等于最大容量
wc >= (core ? corePoolSize : maximumPoolSize)) //线程数大于等于核心线程数或最大线程数 根据传值判断
return false; //直接结束
if (compareAndIncrementWorkerCount(c)) // CAS增加线程数的数量
break retry; //跳出外层循环
c = ctl.get(); // Re-read ctl CAS增加线程数数量失败,再次获取线程池状态
if (runStateOf(c) != rs) // 状态如果变更则说明需要重新获取状态,即跳出内层循环再次执行外循环,没变更继续执行内层循环
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//实际上面这部分就是CAS增加线程数,保证成功 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); //加锁的用意在这,保证并发下hashset插入正确
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;
}

4.核心线程Worker相关

private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
AQS模式 独占模式的AQS 实际也是线程池的线程调度实现方式 /**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker 设置AQS本节点状态是正常调度状态
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//用给定的或默认的线程工厂创建新线程
} /** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);//执行线程start 实际是执行这
}

5.runWorker

final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts 这一步解锁,看AQS知道1初始化本节点状态0,2唤醒AQS的线程调度
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {//获取任务去执行
w.lock();//实际是把本线程放入AQS调度队列中等待调度
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();//线程池不可用的时候中断本线程
try {
beforeExecute(wt, task);//可以实现自己的逻辑
Throwable thrown = null;
try {
task.run();//执行任务
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);//可以实现自己的逻辑
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;//标识是否是异常完成,上面是死循环,没任务了线程中断肯定是false,因异常中断的就是true
} finally {
processWorkerExit(w, completedAbruptly);//主要作用是没任务了,检查是否需要中断本线程
}
}

总结:
线程池实际就是线程安全的用hashset维护一个指定策略的线程集,用于执行提交的任务。其中使用AQS独占模式线程调度

ThreadPoolExecutor 线程池 简单解析的更多相关文章

  1. ThreadPoolExecutor 线程池的源码解析

    1.背景介绍 上一篇从整体上介绍了Executor接口,从上一篇我们知道了Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newSchedul ...

  2. Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式

    前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...

  3. 13.ThreadPoolExecutor线程池之submit方法

    jdk1.7.0_79  在上一篇<ThreadPoolExecutor线程池原理及其execute方法>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法 ...

  4. 手写线程池,对照学习ThreadPoolExecutor线程池实现原理!

    作者:小傅哥 博客:https://bugstack.cn Github:https://github.com/fuzhengwei/CodeGuide/wiki 沉淀.分享.成长,让自己和他人都能有 ...

  5. 源码剖析ThreadPoolExecutor线程池及阻塞队列

    本文章对ThreadPoolExecutor线程池的底层源码进行分析,线程池如何起到了线程复用.又是如何进行维护我们的线程任务的呢?我们直接进入正题: 首先我们看一下ThreadPoolExecuto ...

  6. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  7. j.u.c系列(01) ---初探ThreadPoolExecutor线程池

    写在前面 之前探索tomcat7启动的过程中,使用了线程池(ThreadPoolExecutor)的技术 public void createExecutor() { internalExecutor ...

  8. ThreadPoolExecutor 线程池

    TestThreadPoolExecutorMain package core.test.threadpool; import java.util.concurrent.ArrayBlockingQu ...

  9. 十、自定义ThreadPoolExecutor线程池

    自定义ThreadPoolExecutor线程池 自定义线程池需要遵循的规则 [1]线程池大小的设置 1.计算密集型: 顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CP ...

随机推荐

  1. JWT的优点和实现Token认证的安全问题

    JWT的优点和实现Token认证的安全问题 一.什么是JWT JWT——Json web token  是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,可实现无状态.分布式的Web应 ...

  2. swoole4创建Mysql连接池

    一 .什么是mysql连接池 场景:每秒同时有1000个并发,但是这个mysql同时只能处理400个连接,mysql会宕机.   解决方案:连接池,这个连接池建立了200个和mysql的连接,这100 ...

  3. IT兄弟连 Java语法教程 流程控制语句 控制循环结构1

    Java语言没有提供goto语句来控制程序的跳转,这种做法提高了程序流程控制的可读性,但降低了程序流程控制的灵活性.为了弥补这种不足,Java提供了continue和break来控制循环结构.除此之外 ...

  4. ExtJS布局控件

    Layout Controls Auto Layout Ext JS4中的容器的默认布局是自动布局.这个布局管理器会自动地将组件放在一个容器中. Fit Layout Fit布局安排了容器的内容完全占 ...

  5. 【mysql报错】[Err] 1248 - Every derived table must have its own alias

    当我运行一条联合查询的sql语句时报如下错误: [Err] 1248 - Every derived table must have its own alias,大概意思是每一张派生表必须要有自己的别 ...

  6. java在hashmap初始化时赋初值

    Java中的HashMap是一种常用的数据结构,一般用来做数据字典或者Hash查找的容器. 一般我们初始化并赋初值是这样做的: HashMap<String, Object> map = ...

  7. MYSQL 高级语法

    1.高级建表和插入 使用creat 和select 进行建表操作,中间采用AS 标识符: CREATE TABLE new_table AS SELECT * FROM exist_table LIM ...

  8. [IDA] 将连续的单个变量值修改为结构体

    直接选中最上边的结构体,使用ALT+Q来进行修改.

  9. SPA项目搭建及嵌套路由

    Vue-cli: 什么是vue-cli? vue-cli是vue.js的脚手架,用于自动生成vue.js+webpack的项目模板,创建命令如下: vue init webpack xxx 注1:xx ...

  10. HTML颜色名称大全

    所有浏览器支持的颜色名称,所有现代浏览器都支持以下140种颜色名称(单击颜色名称或十六进制值,以将颜色视为背景颜色以及不同的文本颜色): 有关HTML颜色的完整概述,请访问我们的颜色教程. 颜色名称 ...