keepAliveTime含义
ThreadPoolExecutor
的 javadoc 对 keepAliveTime
参数的解释:
keepAliveTime when the number of threads is greater than the core,
this is the maximum time that excess idle threads will wait for new
tasks before terminating.
大致意思是:keepAliveTime
就是 当线程数大于核心线程数时,在终止前,多余的空闲线程等待新任务的最长时间。
大白话说明
假设,核心线程数10,最大线程数30,keepAliveTime是 3秒
随着任务数量不断上升,线程池会不断的创建线程,直到到达核心线程数10,就不创建线程了,这时多余的任务通过加入阻塞队列来运行,当超出(阻塞队列长度+核心线程数)时,
这时不得不扩大线程个数来满足当前任务的运行,这时就需要创建新的线程了(最大线程数起作用),上限是最大线程数30,
那么,超出核心线程数10并小于最大线程数30的可能新创建的这20个线程相当于是”借”的,如果这20个线程空闲时间超过keepAliveTime,就会被退出。
注:核心线程的关闭 由
ThreadPoolExecutor#allowCoreThreadTimeout
决定,默认 allowCoreThreadTimeout=false,代表不会 超时终止,就是不受 KeepLive的影响。
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* If false (default), core threads stay alive even when idle.
* If true, core threads use keepAliveTime to time out waiting
* for work.
*/
// 全局变量,自动初始化,boolean 类型默认是 false
private volatile boolean allowCoreThreadTimeOut;
// 略
// 设置 核心线程是否可以 超时终止,默认 allowCoreThreadTimeOut=false
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
// 略
}
但是我有两个疑问
-
线程为什么会空闲
没有任务时线程就会空闲下来,在线程池中任务是任务(Runnale)线程是线程(Worker)
-
线程为什么要退出
通常超出核心线程的线程是“借”的,也就是说超出核心线程的情况算是一种能够预见的异常情况,并且这种情况并不常常发生(如果常常发生,那我想你应该调整你的核心线程数了),所以这种不经常发生而创建的线程为了避免资源浪费就应该要退出
我们需要看一下java.util.concurrent.ThreadPoolExecutor#getTask
源码和javadoc 注释来验证上面一段话的含义:
ThreadPoolExecutor#getTask javadoc
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
大致意思:
执行任务阻塞或时间等待,具体取决于当前配置设置,如果此工作线程返回 null
就必须退出,可能是因为以下任何一个原因造成:
- 这里有超过最大线程池大小的工作线程(最大线程池大小 由 setMaximumPoolSize 设置)。
- 线程池已经停止了。
- 线程池已关闭,并且阻塞队列为空。
- 工作线程等待任务,已经超时了,那么超时的工作线程将会被终止。(代码描述:allowCoreThreadTimeOut || workerCount > corePoolSize)
ThreadPoolExecutor#getTask 源码
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
// 当allowCoreThreadTimeOut为true或者当前任务数超过核心线程数时,timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//如果timed为true说明worker有可能要被关闭,
// 这里调用的代码含义:
//如果超过keepAliveTime纳秒内还没取到任务,就返回null,后面会调用processWorkerExit把worker关闭
// 如果timed为false,
// 就阻塞在这里,直到任务队列再有任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
keepAliveTime 测试
-
测试:
设置 线程池 核心线程数=1,最大线程数=1,空闲线程最大存活时间=0(立即终止),阻塞队列长度=1
按循序,立即提交三个任务,依次分别命名为 1,2,3
任务处理时间 3s -
执行步骤:
任务1 首先提交,立即创建线程 ,执行任务1;
任务2 提交时,任务1还未完成,于是,将任务2,保存到 阻塞队列中
任务3 提交,任务1,未完成,任务2 还在阻塞队列中等待,
因为此时已经达到核心线程数,并且阻塞队列已经满了
还没有达到最大线程数,因此,创建新线程执行任务3
任务 1 首先结束
然后,任务3 结束,因为 keepLive=0,所以,执行完任务 3 的线程,将会立即被终止
最后,执行完任务 1 的线程空闲,但是默认核心线程不会超时终止,所以,执行完任务 1 的线程继续继续执行任务 2
任务 2 结束
线程池 shutdown
public class keepAliveTimeTest {
public static void main(String[] args) {
ThreadPoolExecutor pool = null;
try {
// 实例化线程池
pool = new ThreadPoolExecutor(1,2,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1));
// 未提交任何任务的线程池的状态
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
pool.execute(new Task("1"));
pool.execute(new Task("2"));
pool.execute(new Task("3"));
System.out.println("========================================");
// 阻塞 9s,线程池所有任务结束,阻塞 1s,看看核心线程和”借来“的线程的状态
TimeUnit.SECONDS.sleep(10);
System.out.println("========================================");
// 执行完所有任务的线程池的状态
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 等待所有任务结束,才会关闭线程池
assert pool != null;
pool.shutdown();
}
}
static class Task implements Runnable {
private String name;
public Task(String name){
this.name = name;
}
public void run() {
try {
System.out.println(name + " Task running! [" + Thread.currentThread().getName() + "]");
TimeUnit.SECONDS.sleep(3);
System.out.println(name + " Task Completed! ["+ Thread.currentThread().getName() + "]");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试结果:
Core threads: 1
Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
========================================
1 Task running! [pool-1-thread-1]
3 Task running! [pool-1-thread-2]
3 Task Completed! [pool-1-thread-2]
1 Task Completed! [pool-1-thread-1]
2 Task running! [pool-1-thread-2]
2 Task Completed! [pool-1-thread-2]
========================================
Core threads: 1
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 1
Currently executing threads: 0
Total number of threads(ever scheduled): 3
可以看到,Current threads in pool = 1,所以说明,KeepLive=0
时,”借来”的工作线程是不等待直接退出的。而不是永远不退出。
注意: KeepLive设置为0,并不是个很好的做法(除非场景中任务数量极少能超出核心线程数),如果任务数频繁超出核心线程数,这个值需要评估设定为合理值尽量避免线程开启关闭的动作。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69684.html