*/
private volatile int maximumPoolSize;
创建线程池
Executors
提供获取几种常用的线程池的方法:
- 缓存程线程池
newCachedThreadPool
是一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute()
将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor
构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
- 单线程线程池
newSingleThreadExecutor
创建是一个单线程池,也就是该线程池只有一个线程在工作,所有的任务是串行执行的,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
- 固定大小线程池
newFixedThreadPool
创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小,线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory);
}
- 单线程线程池
newScheduledThreadPool
创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
我们可以看出来上面的方法一共使用了 DelayedWorkQueue
、 LinkedBlockingQueue
和 SynchronousQueue
。这个就是线程核心之一的阻塞队列。
任务阻塞队列
它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列;
SynchronousQueue
1、 直接提交队列 :设置为 SynchronousQueue
队列, SynchronousQueue
是一个特殊的 BlockingQueue
,它没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。
使用 SynchronousQueue
队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于 maximumPoolSize
,则尝试创建新的进程,如果达到 maximumPoolSize
设置的最大值,则根据你设置的 handler
执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的 maximumPoolSize
数量,否则很容易就会执行拒绝策略;
ArrayBlockingQueue
2、 有界的任务队列 :有界的任务队列可以使用 ArrayBlockingQueue
实现,如下所示:
pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
使用 ArrayBlockingQueue
有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到 corePoolSize
时,则会将新的任务加入到等待队列中。若等待队列已满,即超过 ArrayBlockingQueue
初始化的容量,则继续创建线程,直到线程数量达到 maximumPoolSize
设置的最大线程数量,若大于 maximumPoolSize
,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在 corePoolSiz
e以下,反之当任务队列已满时,则会以 maximumPoolSize
为最大线程数上限。
LinkedBlockingQueue
3、 无界的任务队列 :无界任务队列可以使用 LinkedBlockingQueue
实现,如下所示:
pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你 corePoolSize
设置的数量,也就是说在这种情况下 maximumPoolSize
这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到 corePoolSize
后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。
PriorityBlockingQueue
4 、优先任务队列: 优先任务队列通过 PriorityBlockingQueue
实现:
任务会按优先级重新排列执行,且线程池的线程数一直为 corePoolSize
,也就是只有一个。
PriorityBlockingQueue
其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过 corePoolSize
的数量,只不过其他队列一般是按照先进先出的规则处理任务,而 PriorityBlockingQueue
队列可以自定义规则根据任务的优先级顺序先后执行。
其实 LinkedBlockingQueue
也是可以设置界限的,它默认的界限是 Integer.MAX_VALUE
。同时也支持也支持构造的时候设置队列大小。
拒绝策略
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
当 Executor
已经关闭,即执行了 executorService.shutdown()
方法后,或者 Executor
将有限边界用于最大线程和工作队列容量,且已经饱和时。使用方法 execute()
提交的新任务将被拒绝.
在以上述情况下, execute
方法将调用其 RejectedExecutionHandler
的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)
方法。
AbortPolicy 默认的拒绝策略
也称为终止策略,遭到拒绝将抛出运行时 RejectedExecutionException
。业务方能通过捕获异常及时得到对本次任务提交的结果反馈。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
CallerRunsPolicy
拥有自主反馈控制,让提交者执行提交任务,能够减缓新任务的提交速度。这种情况是需要让所有的任务都执行完毕。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
DiscardPolicy
拒绝任务的处理程序,静默丢弃任务。使用此策略,我们可能无法感知系统的异常状态。慎用~!
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
DiscardOldestPolicy
丢弃队列中最前面的任务,然后重新提交被拒绝的任务。是否要使用此策略需要看业务是否需要新老的替换,慎用~!
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute®;
}
}
}
内核
–
前面讲了 线程池的外观 ,接下来讲述它的 内核 。
线程池在内部实际上构建了一个 生产者消费者模型 ,将 线程
和 任务
两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。
线程池的运行主要分成两部分:任务管理、线程管理。
任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:
-
直接申请线程执行该任务;
-
缓冲到队列中等待线程执行;
-
拒绝该任务。
线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。
接下来,我们会按照以下三个部分去详细讲解线程池运行机制:
-
线程池如何维护自身状态。
-
线程池如何管理任务。
-
线程池如何管理线程。
线程池的生命周期
线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。
线程池内部使用一个变量维护两个值:运行状态( runState
)和线程数量 ( workerCount
)。
在具体实现中,线程池将运行状态( runState
)、线程数量 ( workerCount
)两个关键参数的维护放在了一起:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl
这个 AtomicInteger
类型,是对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段.
它同时包含两部分的信息:线程池的运行状态 ( runState
) 和线程池内有效线程的数量 ( workerCount
),高 3位 保存 runState
,低 29 位保存 workerCount
,两个变量之间互不干扰。
用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。通过阅读线程池源代码也可以发现,经常出现要同时判断线程池运行状态和线程数量的情况。线程池也提供了若干方法去供用户获得线程池当前的运行状态、线程个数。这里都使用的是位运算的方式,相比于基本运算,速度也会快很多(PS:这种用法在许多源代码中都可以看到)。
关于内部封装的获取生命周期状态、获取线程池线程数量的计算方法如以下代码所示:
private static final int COUNT_BITS = Integer.SIZE - 3;//32-3
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//低29位都为1,高位都为0
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;//111
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
private static final int TERMINATED = 3 << COUNT_BITS;//011
// Packing and unpacking ctl
//计算当前运行状态,取高三位
private static int runStateOf(int c) { return c & ~CAPACITY; }
//计算当前线程数量,取低29位
private static int workerCountOf(int c) { return c & CAPACITY; }
//通过状态和线程数生成ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
ThreadPoolExecutor
的运行状态有5种,分别为:
| 运行状态 | 状态描述 |
| — | — |
| RUNNING | 能接受新提交的任务,并且也能处理阻塞队列中的任务 |
| SHUTDOWN | 不能接受新提交的任务,但却可以继续处理阻塞队列中的任务 |
| STOP | 不能接受新任务,也不能处理队列中的任务同时会中断正在处理的任务线程 |
| TIDYING | 所有的任务都已经终止,workCount(有效线程数)为0 |
| TERMINATED | 在terminated方法执行完之后进入该状态 |
任务调度机制
任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。
首先,所有任务的调度都是由 execute
方法完成的,这部分完成的工作是:检查现在线程池的 运行状态 、 运行线程数 、 运行策略 ,决定接下来执行的流程,是直接 申请线程执行 ,或是 缓冲到队列中执行 ,亦或是 直接拒绝该任务 。其执行过程如下:
-
首先检测线程池运行状态,如果不是
RUNNING
,则直接拒绝,线程池要保证在RUNNING
的状态下执行任务。 -
如果
workerCount < corePoolSize
,则创建并启动一个线程来执行新提交的任务。 -
如果
workerCount >= corePoolSize
,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。 -
如果
workerCount >= corePoolSize && workerCount < maximumPoolSize
,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。 -
如果
workerCount >= maximumPoolSize
,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
接下来进入源代码分析时间~!
提交任务
//AbstractExecutorService.java
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
//ThreadPoolExecutor.java
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();//获取ctl
//检查当前核心线程数,是否小于核心线程数的大小限制
if (workerCountOf© < corePoolSize) {
//没有达到核心线程数的大小限制,那么添家核心线程执行该任务
if (addWorker(command, true))
return;
//如果添加失败,刷新ctl值
c = ctl.get();
}
//再次检查线程池的运行状态,将任务添加到等待队列中
if (isRunning© && workQueue.offer(command)) {
int recheck = ctl.get();//刷新ctl值
//如果当前线程池的装不是运行状态,那么移除刚才添加的任务
if (! isRunning(recheck) && remove(command))
reject(command);//移除成功后,使用拒绝策略处理该任务;
else if (workerCountOf(recheck) == 0)//当前工作线程数为0
//线程池正在运行,或者移除任务失败。
//添加一个非核心线程,并不指定该线程的运行任务。
//等线程创建完成之后,会从等待队列中获取任务执行。
addWorker(null, false);
}
//逻辑到这里说明线程池已经不是RUNNING状态,或者等待队列已满,需要创建一个新的非核心线程执行该任务;
//如果创建失败,那么非核心线程已满,使用拒绝策略处理该任务;
else if (!addWorker(command, false))
reject(command);
}
添加工作线程和执行任务
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;//初始化的任务,可以为null
this.thread = getThreadFactory().newThread(this);//Worker持有的线程
}
/**部分代码省略*/
public void run() {
runWorker(this);
}
}
添加工作线程和执行任务:总体就是创建 Worker
,并且为它找到匹配的 Runnable
。
添加工作线程
增加线程是通过线程池中的 addWorker
方法,该方法的功能就是增加一个线程,该方法不考虑线程池是在哪个阶段增加的该线程,这个分配线程的策略是在上个步骤完成的,该步骤仅仅完成增加线程,并使它运行,最后返回是否成功这个结果。
addWorker
方法有两个参数: firstTask
、 core
。
firstTask
参数用于指定新增的线程执行的第一个任务,该参数可以为空;
core
参数为 true
表示在新增线程时会判断当前活动线程数是否少于 corePoolSize
, false
表示新增线程前需要判断当前活动线程数是否少于 maximumPoolSize
。
private boolean addWorker(Runnable firstTask, boolean core) {
retry://break和continue的跳出标签
for (;😉 {
int c = ctl.get();//获取ctl的值
int rs = runStateOf©;//获取当前线程池的状态;
/**
* 1、如果当前的线程池状态不是RUNNING
* 2、当前线程池是RUNNING而且没有添加新任务,而且等待队列不为空。这种情况下是需要创建执行线程的。
* 所以满足1,但不满足2就创建执行线程失败,返回false。
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
/**进入内层循环 */
for (;😉 {
int wc = workerCountOf©;//获取当前执行线程的数量
/**
* 1、工作线程数量大于或等于计数器的最大阈值,那么创建执行线程失败,返回false。
* 2、如果当前创建的核心线程,那么工作线程数大于corePoolSize的话,创建执行线程失败,返回false。
* 3、如果当前创建的是非核心线程,那么工作线程数大于maximumPoolSize的话,创建执行线程失败,返回false。
*/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//用CAS操作让线程数加1,如果成功跳出整个循环
if (compareAndIncrementWorkerCount©)
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf© != 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());
/**
* 再次检查线程池的运行状态
* 1、如果是RUNNING状态,那么可以创建;
* 2、如果是SHUTDOWN状态,但没有执行线程,可以创建(创建后执行等待队列中的任务)
*/
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//检测该线程是否已经开启
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);//将改工作线程添加到集合中
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;
}
执行任务
在 添加工作线程 部分我们看到了,添加成功之后会开启线程执行任务。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//解锁,允许中断
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果当前的工作线程已经有执行任务,或者可以从等待队列中获取到执行任务
//getTask获取任务时候会进行阻塞
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
//判断线程是否需要中断
//如果线程池状态是否为STOP\TIDYING\TERMINATED,同时当前线程没有被中断那么将当前线程进行中断
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++;//当前工作线程执行完成的线程数+1
w.unlock();//执行完成解锁
}
}
completedAbruptly = false;//完成了所有任务,正常退出
} finally {//执行工作线程的退出操作
processWorkerExit(w, completedAbruptly);
}
}
工作线程获取任务
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;😉 {
int c = ctl.get();//获取ctl的值
int rs = runStateOf©;//获取线程池状态
// Check if queue empty only if necessary.
/**
* 1、rs为STOP\TIDYING\TERMINATED,标识无法继续执行任务
* 2、等待队列中没有任务可以被执行
* 工作线程数量减一
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf©;//获取工作线程数量
// Are workers subject to culling?
//如果允许核心线程超时,或者当前工作线程数量大于核心线程数量。标识需要进行超时检测
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/**
* 1、如果当前工作线程数是否大于线程池可允许的最大工作线程数(maximumPoolSize可以动态设置)
* ,或者当前需要进行超时控制并且上次从等待队列中获取执行任务发生了超时。
* 2、如果当前不是唯一的线程,并且等待队列中没有需要执行的任务。
* 这两种情况下一起存在就表示,工作线程发生了超时需要回收,所以对线程数进行-1;
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount©)//线程数量减少成功,否则重新执行本次循环
return null;
continue;
}
try {
//如果设置有超时,那么设定超时时间。否则进行无限的阻塞等待执行任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;//获取超时,设置标记
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
工作线程的退出
线程池中线程的销毁依赖 JVM自动 的回收,线程池做的工作是根据当前线程池的状态维护一定数量的线程引用,防止这部分线程被JVM回收,当线程池决定哪些线程需要回收时,只需要将其引用消除即可。 Worker
被创建出来后,就会不断地进行轮询,然后获取任务去执行,核心线程可以无限等待获取任务,非核心线程要限时获取任务。当 Worker
无法获取到任务,也就是获取的任务为空时,循环会结束, Worker
会主动消除自身在线程池内的引用。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//completedAbruptly为true,标识该工作线程执行出现了异常,将工作线程数减一
if (completedAbruptly) // If abrupt, then workerCount wasn’t adjusted
decrementWorkerCount();
//否则标识该工作线程为正常结束,这种情况下getTask方法中已经对工作线程进行了减一
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//加锁
try {
completedTaskCount += w.completedTasks;//更新线程池的,线程执行完成数量
workers.remove(w);//工作线程容器移除该工作线程
} finally {
mainLock.unlock();//解锁
}
//尝试结束线程池
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {//如果当前线程池的运行状态是RUNNING\SHUTDOWN
if (!completedAbruptly) {//如果该工作线程为正常结束
/**
* 判断当前需要的最少的核心线程数(如果允许核心线程超时,那么最小的核心线程数为0,否则为corePoolSize)
*/
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//如果允许核心线程超时,而且等待队列不为空,那么工作线程的最小值为1,否则为0。
if (min == 0 && ! workQueue.isEmpty())
min = 1;
//当前工作线程数,是否满足最先的核心线程数
if (workerCountOf© >= min)
//如果满足那么直接return
return; // replacement not needed
}
//如果是异常结束,或者当前线程数不满足最小的核心线程数,那么添加一个非核心线程
//核心线程和非核心线程没有什么不同,只是在创建的时候判断逻辑不同
addWorker(null, false);
}
}
特需
–
线程池的监控
通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用
-
getTaskCount
:线程池已经执行的和未执行的任务总数; -
getCompletedTaskCount
:线程池已完成的任务数量,该值小于等于taskCount
; -
getLargestPoolSize
:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize
; -
getPoolSize
:线程池当前的线程数量; -
getActiveCount
:当前线程池中正在执行任务的线程数量。
动态调整线程池的大小
JDK
允许线程池使用方通过 ThreadPoolExecutor
的实例来动态设置线程池的核心策略,以 setCorePoolSize
为方法例;
在运行期线程池使用方调用此方法设置 corePoolSize
之后,线程池会直接覆盖原来的 corePoolSize
值,并且基于当前值和原始值的比较结果采取不同的处理策略。
对于当前值小于当前工作线程数的情况,说明有多余的 worker
线程,此时会向当前 idle
的 worker
线程发起中断请求以实现回收,多余的 worker
在下次 idel
的时候也会被回收;对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的 worker
线程来执行队列任务(PS: idel
状态为 worker
线程释放锁之后的状态,因为它在运行期间都是上锁的)。
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
//计算增量
int delta = corePoolSize - this.corePoolSize;
//覆盖原有的corePoolSize
this.corePoolSize = corePoolSize;
//如果当前的工作线程数量大于线程池的最大可运行核心线程数量,那么进行中断工作线程处理
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
else if (delta > 0) {//如果增量大于0
// We don’t really know how many new threads are “needed”.
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
//等待队列非空,获取等待任务和增量的最小值
int k = Math.min(delta, workQueue.size());
//循环创建核心工作线程执行等待队列中的任务
while (k-- > 0 && addWorker(null, true)) {
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
ize < 0)
throw new IllegalArgumentException();
//计算增量
int delta = corePoolSize - this.corePoolSize;
//覆盖原有的corePoolSize
this.corePoolSize = corePoolSize;
//如果当前的工作线程数量大于线程池的最大可运行核心线程数量,那么进行中断工作线程处理
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
else if (delta > 0) {//如果增量大于0
// We don’t really know how many new threads are “needed”.
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
//等待队列非空,获取等待任务和增量的最小值
int k = Math.min(delta, workQueue.size());
//循环创建核心工作线程执行等待队列中的任务
while (k-- > 0 && addWorker(null, true)) {
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Vq2A5Hr7-1715761347167)]
[外链图片转存中…(img-qhXphHUv-1715761347167)]
[外链图片转存中…(img-IivtCSzj-1715761347168)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!