0%

线程池线程复用的原理

线程池线程复用的原理是什么

思考这么一个问题:任务结束后会不会回收线程?

答案是:allowCoreThreadTimeOut控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/java/util/concurrent/ThreadPoolExecutor.java:1127
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) {...执行任务...}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
首先线程池内的线程都被包装成了一个个的java.util.concurrent.ThreadPoolExecutor.Worker,然后这个worker会马不停蹄的执行任务,执行完任务之后就会在while循环中去取任务,取到任务就继续执行,取不到任务就跳出while循环(这个时候worker就不能再执行任务了)执行 processWorkerExit方法,这个方法呢就是做清场处理,将当前woker线程从线程池中移除,并且判断是否是异常的进入processWorkerExit方法,如果是非异常情况,就对当前线程池状态(RUNNING,shutdown)和当前工作线程数和当前任务数做判断,是否要加入一个新的线程去完成最后的任务(防止没有线程去做剩下的任务).
那么什么时候会退出while循环呢?取不到任务的时候(getTask() == null).下面看一下getTask方法

private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?

for (;;) {
int c = ctl.get();
int rs = runStateOf(c);

//(rs == SHUTDOWN && workQueue.isEmpty()) || rs >=STOP
//若线程池状态是SHUTDOWN 并且 任务队列为空,意味着已经不需要工作线程执行任务了,线程池即将关闭
//若线程池的状态是 STOP TIDYING TERMINATED,则意味着线程池已经停止处理任何任务了,不在需要线程
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//把此工作线程从线程池中删除
decrementWorkerCount();
return null;
}

int wc = workerCountOf(c);

//allowCoreThreadTimeOut:当没有任务的时候,核心线程数也会被剔除,默认参数是false,官方推荐在创建线程池并且还未使用的时候,设置此值
//如果当前工作线程数 大于 核心线程数,timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

//(wc > maximumPoolSize || (timed && timedOut)):当工作线程超过最大线程数,或者 允许超时并且超时过一次了
//(wc > 1 || workQueue.isEmpty()):工作线程数至少为1个 或者 没有任务了
//总的来说判断当前工作线程还有没有必要等着拿任务去执行
//wc > maximumPoolSize && wc>1 : 就是判断当前工作线程是否超过最大值
//或者 wc > maximumPoolSize && workQueue.isEmpty():工作线程超过最大,基本上不会走到这,
// 如果走到这,则意味着wc=1 ,只有1个工作线程了,如果此时任务队列是空的,则把最后的线程删除
//或者(timed && timedOut) && wc>1:如果允许超时并且超时过一次,并且至少有1个线程,则删除线程
//或者 (timed && timedOut) && workQueue.isEmpty():如果允许超时并且超时过一次,并且此时工作 队列为空,那么妥妥可以把最后一个线程(因为上面的wc>1不满足,则可以得出来wc=1)删除
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
//如果减去工作线程数成功,则返回null出去,也就是说 让工作线程停止while轮训,进行收尾
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;
}
}
}

//综上所述,如果allowCoreThreadTimeOut为true,并且在第1次阻塞获取任务失败了,那么当前getTask会返回null,不管是不是核心线程;那么runWorker中将推出while循环,也就意味着当前工作线程被销毁

通过上面这个问题可以得出一个结论:当你的线程池参数配置合理的时候,执行完任务的线程是不会被销毁的,而是会从任务队列中取出任务继续执行!