ThreadPoolExecutor 线程池的源码解析
正文前先来一波福利推荐:
福利一:
百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。
福利二:
毕业答辩以及工作上各种答辩,平时积累了不少精品PPT,现在共享给大家,大大小小加起来有几千套,总有适合你的一款,很多是网上是下载不到。
获取方式:
微信关注 精品3分钟 ,id为 jingpin3mins,关注后回复 百万年薪架构师 ,精品收藏PPT 获取云盘链接,谢谢大家支持!
-----------------------正文开始---------------------------
1、背景介绍
上一篇从整体上介绍了Executor
接口,从上一篇我们知道了Executor
框架的最顶层实现是ThreadPoolExecutor
类,Executors
工厂类中提供的newScheduledThreadPool
、newFixedThreadPool
、newCachedThreadPool
方法
其实本质上也只是ThreadPoolExecutor
的构造函数参数不同而已。通过传入不同的参数,就可以构造出适用于不同应用场景下的线程池,那么它的底层原理是怎样实现的呢,这篇就来介绍下ThreadPoolExecutor
线程池的运行过程。
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
在ThreadPoolExecutor类中提供了四个构造方法:

public class ThreadPoolExecutor extends AbstractExecutorService {
.....
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}

从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
ThreadPoolExecutor的完整构造方法的签名是:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有 任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者 prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize个线程或者一个线程。
- 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线 程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
- maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
- keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize 时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。即当线程池中的线程数大于corePoolSize 时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了 allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize 时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
- unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒

- workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue; //有界队列
LinkedBlockingQueue; //无界队列
SynchronousQueue; //特殊的一个队列,只有存在等待取出的线程时才能加入队列,可以说容量为1,是无界队列
PriorityBlockingQueueArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
- threadFactory:线程工厂,主要用来创建线程;
- handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务ThreadPoolExecutor是Executors类的底层实现。
在JDK帮助文档中,有如此一段话:
“强烈建议程序员使用较为方便的
Executors
工厂方法Executors.newCachedThreadPool()
(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)
(固定大小线程池)Executors.newSingleThreadExecutor()
(单个后台线程)它们均为大多数使用场景预定义了设置。”
下面介绍一下几个类的源码:
ExecutorService es = executor.newFixedThreadPool (int nThreads):固定大小线程池。
可以看到,corePoolSize和maximumPoolSize的大小是一样的(实际上,后面会介绍,如果使用无界queue的话 maximumPoolSize参数是没有意义的),keepAliveTime和unit的设值表明什么?-就是该实现不想keep alive!最后的BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的,为什么是使用无界的LinkedBlockingqueue 是因为无法知道用户设置多大的固定线程数量,但是数量肯定在无界的范围内。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ExecutorService newSingleThreadExecutor():单线程

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

ExecutorService newCachedThreadPool():无界线程池,可以进行自动线程回收
这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。可能对于该BlockingQueue有些陌生,简单说:该 QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
从上面给出的ThreadPoolExecutor类的代码可以知道,ThreadPoolExecutor继承了AbstractExecutorService,AbstractExecutorService是一个抽象类,它实现了ExecutorService接口。
public abstract class AbstractExecutorService implements ExecutorService
而ExecutorService又是继承了Executor接口
public interface ExecutorService extends Executor
Executor
|-- ExecutorService
|-- AbstractExecutorService
|-- ThreadPoolExecutor
我们看一下Executor接口的实现:
public interface Executor {
void execute(Runnable command);
}
到这里,大家应该明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系了。
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;
然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
然后ThreadPoolExecutor继承了类AbstractExecutorService。
在ThreadPoolExecutor类中有几个非常重要的方法:

public void execute(Runnable command) public <T> Future<T> submit(Callable<T> task) public void shutdown() public List<Runnable> shutdownNow() //返回未执行的任务

execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中 并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的 实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容已在其他文章写过)。
shutdown()和shutdownNow()是用来关闭线程池的。
2、具体源码实现
既然要讲运行过程,那么首先要了解下线程池的状态分为哪些?
volatile int runState;
static final int RUNNING = 0;
static final int SHUTDOWN = 1;
static final int STOP = 2;
static final int TERMINATED = 3;
ThreadPoolExecutor
代码中定义了上面几个变量:定义了一个volatile变量runState,以及其他几个表示状态的常量。runState
:初始状态,表示当前线程池的运行状态,它的值就是上面的那4个常量值之一
RUNNING
:线程池接受新任务并执行队列任务中...
SHUTDOWN
:不再接受新任务,但是会继续执行等待队列Queued中的任务。当调用了shutdown()方法,会从 RUNNING -> SHUTDOWN
STOP
:不再接受新任务,同时也不执行等待队列Queued中的任务,并且会尝试终止正在执行中的任务。当调用了shutdownNow()方法, 会从(RUNNING or SHUTDOWN) -> STOP
TERMINATED
:线程池中所有线程已经停止运行,其他行为同 STOP状态。
- 当等待队列和线程池为空时,会从SHUTDOWN -> TERMINATED
- 当线程池为空时,会从STOP -> TERMINATED
2.线程池运行任务
2.1变量介绍
在讲解运行过程前,我们先看下ThreadPoolExecutor
中的几个比较重要的成员变量:
private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来保存等待中的任务,等待worker线程空闲时执行任务
private final ReentrantLock mainLock = new ReentrantLock(); //更新 poolSize, corePoolSize,maximumPoolSize, runState, and workers set 时需要持有这个锁
private final HashSet<Worker> workers = new HashSet<Worker>(); //用来保存工作中的执行线程
private volatile long keepAliveTime; //超过corePoolSize外的线程空闲存活之间
private volatile boolean allowCoreThreadTimeOut; //是否对corePoolSize内的线程设置空闲存活时间
private volatile int corePoolSize; //核心线程数
private volatile int maximumPoolSize; //最大线程数(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int poolSize; //线程池中的当前线程数
private volatile RejectedExecutionHandler handler; //任务拒绝策略
private volatile ThreadFactory threadFactory; //线程工厂,用来新建线程
private int largestPoolSize; //记录线程池中出现过的最大线程数大小
private long completedTaskCount; //已经执行完的线程数
这边重点解释下 corePoolSize
、maximumPoolSize
、workQueue
两个变量,这两个变量涉及到线程池中创建线程个数的一个策略。corePoolSize
: 这个变量我们可以理解为线程池的核心大小,举个例子来说明(corePoolSize假设等于10,maximumPoolSize等于20):
有一个部门,其中有10(corePoolSize)名工人,当有新任务来了后,领导就分配任务给工人去做,每个工人只能做一个任务。
当10个工人都在忙时,新来的任务就要放到队列(workQueue)中等待。
当任务越积累越多,远远超过工人做任务的速度时,领导就想了一个办法:从其他部门借10个工人来,借的数量有一个公式(maximumPoolSize - corePoolSize)来计算。然后把新来的任务分配给借来的工人来做。
但是如果速度还是还不急的话,可能就要采取措施来放弃一些任务了(RejectedExecutionHandler)。
等到一定时间后,任务都完成了,工人比较闲的情况下,就考虑把借来的10个工人还回去(根据keepAliveTime判断)
也就是说corePoolSize就是线程池大小,maximumPoolSize在我看来是线程池的一种补救措施,即任务量突然过大时的一种补救措施。
2.2线程执行过程
先看下前一篇文章中的一个例子:
ExecutorService executor = Executors.newFixedThreadPool(3); IntStream.range(0, 6).forEach(
i -> executor.execute(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("finished: " + threadName);
}
) );
上面代码就是新建6个任务,然后扔到线程池中运行,输出线程名称,直到运行完毕。其中最核心的方法就是execute()
方法,虽然submit()
也可以执行任务,但它底层也是调用execute()
方法,所以懂了execute()
的实现原理即可:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { //1.
if (runState == RUNNING && workQueue.offer(command)) { //2.
if (runState != RUNNING || poolSize == 0) //3.
ensureQueuedTaskHandled(command); //4.
}
else if (!addIfUnderMaximumPoolSize(command)) //5.
reject(command); // is shutdown or saturated //
}
}
上面的代码看起来逻辑有点复杂,我们一个一个看,首先看上面1位置处:if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))
是一个或表达式,它分成两部分
- 首先判断当前线程数是否大于等于核心线程数,是的话直接进入if语句块中,否则判断第二个部分
- 第二个部分
addIfUnderCorePoolSize(command)
,这个方法是当线程数小于核心线程数时,用来新建线程执行任务(因为线程数小于corePoolSize时,直接新建线程来运行任务,不管当前线程池里有没有空闲的线程)。如果新建失败,那么进入if语句块,成功了那么execute方法就执行结束了,因为线程已经新建成功了,任务已经开始在线程池中运行。
进入if语句块后,看上面代码2.if (runState == RUNNING && workQueue.offer(command))
- 判断当前线程池状态是否是RUNNING 而且 任务放入等待队列中成功,那么直接进入if语句块
- 否则到代码5.处
if (!addIfUnderMaximumPoolSize(command))
,判断新任务用新线程执行是否成功(注:这里的新线程就是我们上面讲的 “借来的工人” maximumPoolSize) - 如果“借来的工人”还是处理不了的话,执行任务拒绝策略
继续进到代码块3 的if语句块if (runState != RUNNING || poolSize == 0)
, 因为新任务加入到等待队列中了,这句判断是为了防止在将此任务添加进任务缓存队列的同时其他线程突然调用shutdown或者shutdownNow方法关闭了线程池的一种应急措施。如果是的话,应急处理加入的新任务 ensureQueuedTaskHandled(command)
。
我们看下两个关键方法的实现:
##### 1.addIfUnderCorePoolSize
private boolean addIfUnderCorePoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < corePoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
return t != null;
}
首先获取锁,因为涉及到线程池状态的变化。然后再次判断 if (poolSize < corePoolSize && runState == RUNNING)
,在execute()方法中我们已经判断过一次,这边再次判断是为了防止其他线程又新增了新线程或者调用了shutdown、shutdownNow方法,这边起到了双重检查的一个效果。如果为true
的话,进行t = addThread(firstTask)
新增线程执行任务。addThread方法里面比较简单,就是通过线程工厂创建线程thread,然后封装到Worker对象中,加入到 workers队列中,并执行线程,可以把Worker对象看成是拥有一个线程的对象。
private Thread addThread(Runnable firstTask) {
Worker w = new Worker(firstTask);
Thread t = threadFactory.newThread(w);
boolean workerStarted = false;
if (t != null) {
w.thread = t;
workers.add(w); //用来保存工作中执行的线程
int nt = ++poolSize;
if (nt > largestPoolSize)
largestPoolSize = nt;
try {
t.start(); //执行的是Worker对象中的run方法 Worker类实现了Runable接口
workerStarted = true;
}
}
return t;
}
这里在介绍下Worker对象, 它实现了Runnable接口,你把它当成Runnable的一个代理类即可,最终也是执行它的run方法。只要注意一下Worker中的beforeExecute
和afterExecute
方法,这两个方法在ThreadPoolExecutor中没有具体实现,用户可以重写这个方法和后面的afterExecute方法来进行一些统计信息,比如某个任务的执行时间等,而afterExecute方法还有一个Throwable t
参数,用户可以用来记录一些异常信息,因为新线程中的异常时捕获不到的,需要在afterExecute中记录。
看起来这个是不是和spring 切面有点像,可以看到 知识都是相通的。
看一下它的run方法:
public void run() {
try {
hasRun = true;
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) { //1
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
注意代码块1,可以看到这边在循环获取任务,并执行,直到任务全部执行完毕。除了第一个任务,其他任务都是通过getTask()
方法去取,这个方法是ThreadPoolExecutor中的一个方法。我们猜一下,整个类中只有任务缓存队列中保存了任务,应该就是去缓存队列中取了。
Runnable getTask() {
for (;;) {
try {
int state = runState;
if (state > SHUTDOWN)
return null;
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll(); //取任务
else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果线程数大于核心池大小或者允许为核心池线程设置空闲时间,
//非核心线程通过poll取任务,若等待一定的时间取不到任务,则返回null
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); //制定时间过后再去进行poll取值
else
r = workQueue.take(); //阻塞取值
if (r != null)
return r;
if (workerCanExit()) { //如果没取到任务,即r为null,则判断当前的worker是否可以退出
if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers(); //中断处于空闲状态的worker
return null;
}
// Else retry
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
}
这里有一个非常巧妙的设计方式,假如我们来设计线程池,可能会有一个任务分派线程,当发现有线程空闲时,就从任务缓存队列中取一个任务交给 空闲线程执行。但是在这里,并没有采用这样的方式,因为这样会要额外地对任务分派线程进行管理,无形地会增加难度和复杂度,这里直接让执行完任务的线程Worker去任务缓存队列里面取任务来执行,因为每一个Worker里面都包含了一个线程thread,如果取到则进行处理,如果取不到则会阻塞在那指导取到任务去执行;
工作队列workQueue会一直去拿任务,属于核心线程的会一直卡在 workQueue.take()方法,直到拿到Runnable 然后返回,非核心线程会 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,如果超时还没有拿到,下一次循环判断compareAndDecrementWorkerCount就会返回null,Worker对象的run()方法循环体的判断为null,任务结束,然后线程被系统回收
下面看一下另一个方法 runTask()
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
/*
* If pool is stopping ensure thread is interrupted;
* if not, ensure thread is not interrupted. This requires
* a double-check of state in case the interrupt was
* cleared concurrently with a shutdownNow -- if so,
* the interrupt is re-enabled.
*/
//当线程池的执行状态为关闭等,则执行当前线程的interrupt()操作
if ((runState >= STOP ||(Thread.interrupted() && runState >= STOP)) && hasRun)
thread.interrupt();
/*
* Track execution state to ensure that afterExecute
* is called only if task completed or threw
* exception. Otherwise, the caught runtime exception
* will have been thrown by afterExecute itself, in
* which case we don't want to call it again.
*/
boolean ran = false;
beforeExecute(thread, task);
try {
//任务执行
task.run();
ran = true;
afterExecute(task, null);
++completedTasks;
} catch (RuntimeException ex) {
if (!ran)
afterExecute(task, ex);
throw ex;
}
} finally {
runLock.unlock();
}
}
把上边的方法放在一个整体代码中看,理解起来更容易一些,代码如下:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
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();
} 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);
}
}
还有一个方法是 每执行完一个线程任务 会调用 workerDone
//记录执行任务数量,将工作线程移除,当poolSize为0是则尝试关闭线程池
void workerDone(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
if (--poolSize == 0)
tryTerminate();
} finally {
mainLock.unlock();
}
2. addIfUnderMaximumPoolSize
这个方法的实现思想和 addIfUnderCorePoolSize方法的实现思想非常相似,唯一的区别在于addIfUnderMaximumPoolSize方法是在线程 池中的线程数达到了核心池大小并且往任务队列中添加任务失败的情况下执行的:
private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < maximumPoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
return t != null;
}
到这里,大部分朋友应该对任务提交给线程池之后到被执行的整个过程有了一个基本的了解,下面总结一下:
- 首先,要清楚corePoolSize和maximumPoolSize的含义;
- 其次,要知道Worker是用来起到什么作用的;
- 要知道任务提交给线程池之后的处理策略,这里总结一下主要有4点:
- 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
- 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
- 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
- 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于 corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2da2gtpfllwkk
ThreadPoolExecutor 线程池的源码解析的更多相关文章
- linux线程池thrmgr源码解析
linux线程池thrmgr源码解析 1 thrmgr线程池的作用 thrmgr线程池的作用是提高程序的并发处理能力,在多CPU的服务器上运行程序,可以并发执行多个任务. 2 ...
- Java并发包源码学习系列:线程池ScheduledThreadPoolExecutor源码解析
目录 ScheduledThreadPoolExecutor概述 类图结构 ScheduledExecutorService ScheduledFutureTask FutureTask schedu ...
- 一个python线程池的源码解析
python为了方便人们编程高度封装了很多东西,比如进程里的进程池,大大方便了人们编程的效率,但是默认却没有线程池,本人前段时间整理出一个线程池,并进行了简单的解析和注释,本人水平有限,如有错误希望高 ...
- 深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- java线程池ThreadPoolExector源码分析
java线程池ThreadPoolExector源码分析 今天研究了下ThreadPoolExector源码,大致上总结了以下几点跟大家分享下: 一.ThreadPoolExector几个主要变量 先 ...
- [转载] Java线程池框架源码分析
转载自http://www.linuxidc.com/Linux/2014-11/108791.htm 相关类Executor,Executors,AbstractExecutorService,Ex ...
- ThreadPoolExecutor(线程池)源码分析
1. 常量和变量 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 高3位为线程池的运行状态,低29 ...
- Java调度线程池ScheduledThreadPoolExecutor源码分析
最近新接手的项目里大量使用了ScheduledThreadPoolExecutor类去执行一些定时任务,之前一直没有机会研究这个类的源码,这次趁着机会好好研读一下. 该类主要还是基于ThreadPoo ...
- Java线程池底层源码分享和相关面试题(持续更新)
线程池各个参数讲解 public ThreadPoolExecutor(int corePoolSize, //线程池核心工作线程数量,比如newFixedThreadPool中可以自定义的线程数量就 ...
随机推荐
- centos6安装cas5
cas是Central Authentication Service的缩写,中文为中央认证服务,在这里我就不说理论了,在公司里项目研发需要cas平台,所以经过两天研究,搞了一个简化版的cas服务,有不 ...
- ubuntu 16.04 清空log文件的方法
由于ubuntu日志文件syslog 和 kern.log 时刻在增长,一会儿就使得根目录文件夹不够用了,需使用如下命令清理 sudo -i 然后输入密码,执行: echo > /var/log ...
- ES6的Promise
推荐一下我觉得不错关于Promise的好文章,通俗易懂 说起ES6的Promise就要提及一下JQ的$.when()方法,两者基本相同 面试的时候经常会问Promise,如果同学们能在回答Promis ...
- Vue 加载外部js文件
Vue.js 加载外部js文件 在项目内新建一个config.js //变量的定义 export var config = { baseurl:'http://172.16.114.5:8088/M ...
- Go Slice 使用中的小陷阱
go中的切片有两个特性: 底层的数据结构是数组 type slice struct { array unsafe.Pointer len int cap int } 包括从切片和数组派生出来的切片都是 ...
- maven的标准pom.xml详解
maven是构建和管理理项目的利器,pom.xml 是其核心.一个标准的pom.xml该怎么写?其中的标签又有什么意义?话不多说,请看代码: <?xml version="1.0&qu ...
- 微信小程序 获得用户输入内容
在微信小程序里,如何获得用户输入的内容?? js: document.getElementById("Content").value jq:$("#Content&quo ...
- 使用 Infer.NET 进行概率编程
本文介绍了如何使用 Infer.NET 进行概率性编程. 概率性编程是一种将自定义模型表示为计算机程序的机器学习方法. 借助它可以在模型中包含专业知识,使机器学习系统更易理解. 它还支持在线推断,即在 ...
- Log4Net 生成多个文件、文件名累加解决方法
Log4Net 生成多个文件.文件名累加解决方法 项目中的WCF服务里采用Log4Net用来记录异常日志,但部署后,生成的日志会出现多个累加文件,如下图: WCF是寄宿在IIS上,包含了5个SVC服务 ...
- 【干货】利用MVC5+EF6搭建博客系统(四)(上)前后台页面布局页面实现,介绍使用的UI框架以及JS组件
一.博客系统进度回顾以及页面设计 1.1页面设计说明 紧接前面基础基本完成了框架搭建,现在开始设计页面,前台页面设计我是模仿我博客园的风格来设计的,后台是常规的左右布局风格. 1.2前台页面风格 主页 ...