ThreadPoolExecutor类:

ThreadPoolExecutor是我们最常用的一个线程池类,它实现了AbstractExecutorService接口。首先来看一下它的构造器及相关关键变量:
    // 这是其中的一个构造器,包含了线程池构造器的七大核心参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
this.corePoolSize = corePoolSize; // 核心线程数
this.maximumPoolSize = maximumPoolSize; // 最大线程数
this.workQueue = workQueue; // 任务队列,可选择链表LinkedBlockingQueue或数组ArrayBlockingQueue实现
this.keepAliveTime = unit.toNanos(keepAliveTime); // 非核心线程的存活时间,若要指定核心线程的存活时间,需设置allowCoreThreadTimeOut,// 存活时间单位
this.threadFactory = threadFactory; // 线程工厂,一般使用Executors工具类中的defaultThreadFactory
this.handler = handler; // 拒绝策略,包括四种,1. AbortPolicy抛出异常;2. CallerRunsPolicy由主线程自己执行任务;3. DiscardPolicy 直接丢弃任务;4. DiscardOldestPolicy 丢弃队列中等待时间最长的任务
} // 关键变量:
// ctl是ThreadPoolExecutor类中的一个状态标记字段,使用int类型AtomicInteger类型值存放,其中高三位存放运行状态,低29位存放线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 运行状态存放在int类型高3位,共有五种状态:RUNNING,SHUDOWN,STOP,TIDYING,TERMINATED;注意其中只有RUNNING状态小于0;后面可据此判断是否为RUNNING状态。
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
下面这张图介绍了ThreadPoolExecutor线程池的任务执行流程,即execute方法的执行流程

通过对比上图的执行流程,我们了解一下execute方法
    public void execute(Runnable command) {
int c = ctl.get();
// 如果当前工作线程小于核心线程,则新增工作线程并返回
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
} if (isRunning(c) && workQueue.offer(command)) { // 如果当前执行器是running状态,并且将任务放入队列中
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) // 再次判断,如果执行器状态不是running,则将队列中的任务移除,并执行拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0) // 如果工作线程数为0,则添加非核心线程
addWorker(null, false);
} else if (!addWorker(command, false)) //如果添加非核心线程失败,则执行拒绝策略
reject(command);
}
相信通过上图结合方法中的注释内容,我们大体上了解了任务的执行流程,接着我们进一步了解一下addWorker方法:

private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (int c = ctl.get();;) {
// 如果执行器状态至少为SHUTDOWN并且运行状态至少为STOP或者任务不为空或者任务队列为空,则直接返回false
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false; for (;;) {
// 如果当前线程数已经大于等于核心/最大线程数,则直接返回false
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
if (compareAndIncrementWorkerCount(c)) // 如果CAS工作线程加1成功,则跳出循环
break retry;
c = ctl.get(); // 再次获取ctl值
if (runStateAtLeast(c, SHUTDOWN)) // 如果执行器状态至少为SHUTDOWN,则继续执行循环
continue retry;
}
} 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 {
int c = ctl.get();
// 若为运行状态,workers数加一
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // workers数添加成功了,则启动该线程执行任务
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 线程启动失败,则移除workers中的该线程,同时ctl减一
}
return workerStarted;
}
由上面的addWork方法我们可以知道,若添加worker则执行run方法,而查看源码我们可以知道,在run方法中调用的是runWork方法,下面我们继续来看一下runWork方法:

// 任务执行主循环体
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) { // 存在未执行的任务
w.lock();
// 条件一. 线程至少处于STOP状态或者线程被中断并且至少处于STOP状态
// 条件二. 该线程没有被中断标记
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt(); // 中断该线程
try {
beforeExecute(wt, task); // 钩子方法,可自定义子类实现,清除ThreadLocals或者执行日志记录
try {
task.run();
afterExecute(task, null); // 钩子方法,自定义子类实现
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
在上面的runWork中采用了while的死循环方式,只要有未完成的任务就去执行,在这里是通过getTask方法来不断的获取队列中的任务,那接下来我们看一下getTask方法:

// 阻塞或定时等待获取任务
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
// 如果执行器至少为SHUTDOWN状态,并且执行器至少到了STOP状态或者任务队列为空时,worker数减一,返回null值
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 如果worker数大于最大线程数或者超出存活时间,则线程数减一,返回null
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
} try {
Runnable r = timed ? // 若线程数大于核心线程数,则有存活时间的等待获取队列中的任务,否则阻塞take任务
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
经过上面的流程,大体上可以了解了ThreadPoolExecutor线程池的执行过程。

JUC多线程之ThreadPoolExecutor类任务执行流程的更多相关文章

  1. Java基础-进程与线程之Thread类详解

    Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...

  2. Java类初始化执行流程

    测试代码: package com.test.ClassLaoderTest; public class test1 { public static String s_variable = " ...

  3. java多线程之ThreadPoolExecutor

    ThreadPoolExecutor类 简介   java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须 ...

  4. Java多线程之ThreadPoolExecutor详解使用

    1.概述 我将讲解JAVA原生线程池的基本使用,并由此延伸出JAVA中和线程管理相关的类结构体系,然后我们详细描述JAVA原生线程池的结构和工作方式 2.为什么要使用线程池 前文我们已经讲到,线程是一 ...

  5. 多线程之ThreadLocal类

    深入研究java.lang.ThreadLocal类 0.前言 ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换 ...

  6. 多线程之Parallel类

    Parallel类是对线程的一个抽象.该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性. Paraller类定义了数据并行地For和ForEach的静态方法,以及 ...

  7. C# 多线程之Thread类

    使用System.Threading.Thread类可以创建和控制线程. 常用的构造函数有:   // 摘要: // 初始化 System.Threading.Thread 类的新实例,指定允许对象在 ...

  8. 多线程之Thread类

    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...

  9. 多线程之Tread类和Runnable的区别

    一.run()方法和start()方法的区别 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继 ...

随机推荐

  1. 初始C3P0连接池

    C3P0连接池只需要一个jar包: 其中我们可以看到有三个jar包: 属于C3P0的jar包只有一个,另外两个是测试时使用的JDBC驱动:一个是mysql的,一个是oracle的: 可以看到在src下 ...

  2. FeignClient注解属性configuration不生效问题排查思路

    FeignClient注解属性configuration不生效问题排查思路 问题背景 我们知道,"如果需要自定义单个Feign配置,Feign的@Configuration 注解的类不能与@ ...

  3. NOIP模拟21:「Median·Game·Park」

    T1:Median   线性筛+桶+随机化(??什么鬼?).   首先,题解一句话秀到了我: 考虑输入如此诡异,其实可以看作随机数据   随机数据??   这就意味着分布均匀..   又考虑到w< ...

  4. 未能找到源类型“DbSet<T>”的查询模式的实现。未找到“Select”

    使用EF6.0的模型优先模式进行开发,遇到了报错,如下图 后来发现是没引用using System.Linq; 引用后就不报错了

  5. openswan协商流程之(二):main_inI1_outR1()

    主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ...

  6. Python中正则表达式简介

    目录 一.什么是正则表达式 二.正则表达式的基础知识 1. 原子 1)普通字符作为原子 2)非打印字符作为原子 3) 通用字符作为原子 4) 原子表 2. 元字符 1)任意匹配元字符 2)边界限制元字 ...

  7. 分享一则Linux系统邮件提示 /usr/local/lib/libprocesshider.so > /etc/ld.so.preload 的中病毒解决方法

    ​ 系统环境:CentOS Linux release 7.6.1810 (AltArch)                   CPU架构:ARM            最近发现生产服务器CPU占用 ...

  8. liquibase新增字段注释导致表格注释同时变更bug记录

    liquibase是一个用于数据库变更跟踪.版本管理和自动部署的开源工具.它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍. 1. 问题复现 在使用过程中发现了一个版本bug.这个b ...

  9. 作业帮-PHP技术一面整理

    项目经验介绍 RPC 调用的协议 用amf 和 base64编码 我想问的是通信协议:调用rpc接口时的过程是什么样?比如业务调用PHP接口的时候,用的是什么协议? (没理解)(https://www ...

  10. 制作Windows服务和安装程序(C#版)

    http://blog.sina.com.cn/s/blog_5f4ffa170100vt2b.html 1.创建服务项目: 打开VS 2005 编程环境,在C#中新建Windows服务程序 2.将安 ...