线程池

假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率。线程池能够对线程进行统一的分配,通过固定数量的线程来负责处理任务,避免了频繁的创建和销毁对象,使线程能够重复的利用,执行多个任务。

Java线程池的结构

Executor 最顶层接口,仅有execute方法。真正的线程池接口应该是它的子接口ExecutorService

ExecutorService接口,主要对Executor接口补充了一些方法,例如shutdown()、submit()等方法

ThreadPoolExecutor ExecutorService的默认实现,作为自定义线程池的主要类。

ScheduledExecutorService 用来解决任务重复执行的问题

ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

ThreadPoolExecutor

ThreadPoolExecutor使ExecutorService的默认实现,其功能也最为基础,通过对该类的源码解析来了解整个线程池的工作机制。

ThreadPoolExecutor最完整的构造器

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 当线程产生的数量多于core时候,空闲线程在这个时间如果没有新任务将被终止

unit keepAliveTime的时间单位

workQueue 线程工作任务队列。任务被执行前保存至工作队列 常用的有ArrayBlockingQueue、LinkedBlockingQuene、priorityBlockingQuene等等

threadFactory 执行程序创建新线程时使用的工厂,默认为DefaultThreadFactory

handler 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序,主要有四种AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

工作流程图如图

源码解析

首先,来看一下几个变量定义

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits
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; // 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; }

线程池使用一个整型变量的高三位作为线程池的状态。

高位111为RUNNING 注:RUNNING为负数,最小

高位000为SHUTDOWN

高位001表示STOP

高位010表示TIDYING

高位100表示TERMINATED

workerCountOf方法用来取低29位的数值,返回线程池的线程数。

runStateOf方法则用来取高3位的数值,返回当前线程池的状态。

然后,来看用于提交任务的submit方法。

public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}

该方法将Runnable进行了封装,我们这里先不考虑,先看看执行的关键方法execute();

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) { // workerCountOF()方法获取当前线程数量,若线程数量小于核心线程数的时候 直接进入addWorker 启动运行
if (addWorker(command, true))
return;
c = ctl.get();
}
// 上面是当线程数量小于core的时候,下面是针对线程数量大于core
if (isRunning(c) && workQueue.offer(command)) { //若线程池处于运行状态,则添加到阻塞队列中
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //recheck 若线程池已经关闭,就remove任务
reject(command); //执行拒绝策略
else if (workerCountOf(recheck) == 0) //线程池处于running状态,但是没有线程,则创建线程
addWorker(null, false);
}
else if (!addWorker(command, false)) // 线程池关闭或者往workQueue提交任务失败,则reject任务
reject(command);
}

execute方法中可以看到,addWork作为一个创建线程并执行任务的重要方法

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 说明线程池不是running; 当线程池关闭,任务为null,workQueue不为空的时候才可以往下进行
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize)) // core为true 则超过core添加失败
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; //获取 worker 中的线程对象,Worker的构造方法会调用 ThreadFactory 来创建一个新的线程
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)) { // 线程池运行或者 线程池关闭 任务为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) //启动失败则把w从works中移除
addWorkerFailed(w);
}
return workerStarted;
}

接下来看看Work类中的run方法,中的runWorker方法

public void run() {
runWorker(this);
}
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) { //getTask方法不断从阻塞队列中获取任务
w.lock();
// 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(); //执行任务的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;
} finally {
processWorkerExit(w, completedAbruptly);
}
}

三种常见线程池

Executors类中为我们提供了集中常见的线程池,分别有FixedThreadPool、SingleThreadExecutor、CachedThreadPool三种

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

FixedThreadPool顾名思义 可以设置固定数量的线程池。但是LinkedBlockingQueue是无界阻塞队列,队列可存放的大小为Integer.MAX_VALUE,因此maximumPoolSize和keepAliveTime将会是个无用参数,拒绝策略也没用

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

同上,不过线程池大小仅为1

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

线程池的线程数可达到Integer.MAX_VALUE。使用SynchronousQueue作为阻塞队列。newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;

Java线程池源码解析的更多相关文章

  1. 【图灵学院10】高并发之java线程池源码分析

    1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...

  2. java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  3. 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇

    大家好,这篇文章我们来聊下动态线程池开源项目(DynamicTp)的通知告警模块.目前项目提供以下通知告警功能,每一个通知项都可以独立配置是否开启.告警阈值.告警间隔时间.平台等,具体代码请看core ...

  4. Java线程池源码及原理

    目录 1 说明 1.1类继承图 2 线程池的状态 3 源码分析 3.1完整的线程池构造方法 3.2 ctl 3.3 任务的执行 3.3.1 execute(Runnable command) 3.3. ...

  5. java线程池源码的理解

    线程池 新建线程和切换线程的开销太大了,使用线程池可以节省系统资源. 线程池的关键类:ThreadPoolExecutor. 该类中包含了大量的多线程与并发处理工具,包括ReentrantLock.A ...

  6. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  7. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  8. 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理

    一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...

  9. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

随机推荐

  1. 我们距离AI编程还有多远?

    近几年来,人工智能的信息以不同形式霸占着我们的眼球,我们知道AlphaGo.微软小冰.Sophia,了解过自动驾驶.无人机.智能家居等,深知人工智能是在记忆力.学习力.运算力方面都远超人类的存在,但人 ...

  2. 使用sftp操作文件并添加事务管理

    本文主要针对文件操作的事务管理,即写文件和删除文件并且能保证事务的一致性,可与数据库联合使用,比如需要在服务器存文件,相应的记录存放在数据库,那么数据库的记录和服务器的文件数一定是要一一对应的,该部分 ...

  3. Django-2- 模板路径查找,模板变量,模板过滤器,静态文件引用

    模板路径查找 路径配置 2. templates模板查找有两种方式 2.1 - 在APP目录下创建templates文件夹,在文件夹下创建模板 2.2 - 在项目根目录下创建templates文件夹, ...

  4. 每日PA -2019年1月帖-每天更新

    开篇 "每日PA"有什么亮点?

  5. c++11の顺序容器

      容器是一种容纳特定类型对象的集合.C++的容器可以分为两类:顺序容器和关联容器.顺序容器的元素排列和元素值大小无关,而是由元素添加到容器中的次序决定的.标准库定义了三种顺序容器的类型:vector ...

  6. python 中 try catch finally语句中含有return语句的执行情况

    无论是在try还是在except中,遇到return时,只要设定了finally语句,就会中断当前的return语句,跳转到finally中执行,如果finally中遇到return语句,就直接返回, ...

  7. 【故障公告】推荐系统中转站撑爆服务器 TCP 连接引发的故障

    上周五下午,我们在博客中部署了推荐系统,在博文下方显示“最新IT新闻”的地方显示自动推荐的关联博文.我们用的推荐系统是第四范式的推荐服务,我们自己只是搭建了一个推荐系统中转站(基于 ASP.NET C ...

  8. jeecg入门操作—模板配置(录入界面)

    点击online表单的模板配置,进入模板设计列表页面,点击创建模板 点击创建模板 点击激活 设计完成,点击激活(表单模板可以多个,激活状态只能有一个) 激活后,重新加入功能测试,点击添加页面,效果如下 ...

  9. Swagger 报错 no mapping found for http request with uri [/***/swagger-ui.html] in dispatcherservlet with name '***'

    swagger报错: no mapping found for http request with uri [/***/swagger-ui.html] in dispatcherservlet wi ...

  10. vue项目接口域名动态获取

    需求: 接口域名是从外部 .json 文件里获取的. 思路: 在开始加载项目前 进行接口域名获取,然后重置 接口域名的配置项. 实现: 1.config/index.js 文件 进行基础配置 impo ...