并发编程之ThreadPoolExecutor源码解析

本节解析要点如下:

1、线程池的执行原理

2、线程是如何复用的

3、核心线程数如何一直保持、最大线程数如何释放

4、线上环境线程池应该定义多大

一 线程池初始化原理

可参考博客:https://www.jianshu.com/p/23cb8b903d2c

 1 public void execute(Runnable command) {
 2          //如果提交的任务为null  抛出空指针异常*/
 3         if (command == null)
 4             throw new NullPointerException();
 5            
 6         int c = ctl.get();
 7         //如果当前的任务数小于等于设置的核心线程大小,那么调用addWorker直接执行该任务*/
 8         if (workerCountOf(c) < corePoolSize) {
 9             if (addWorker(command, true))
10                 return;
11             c = ctl.get();
12         }
13         //如果当前的任务数大于设置的核心线程大小,而且当前的线程池状态时运行状态,那么向阻塞队列中添加任务*/
14         if (isRunning(c) && workQueue.offer(command)) {
15             int recheck = ctl.get();
16             if (! isRunning(recheck) && remove(command))
17                 reject(command);
18             else if (workerCountOf(recheck) == 0)
19                 addWorker(null, false);
20         }
21         //如果向队列中添加失败,那么就新开启一个线程来执行该任务
22         else if (!addWorker(command, false))
23             reject(command);
24 }
View Code

核心要点如下:

addWorker(Runnable firstTask, boolean core)->w = new Worker(firstTask)->t.start()->runWorker(Worker w)->getTask()->task.run()->addWorker(null, false)

二 线程如何复用

请看runWorker方法:runWorker方法里有两个重要方法:getTask() 和 processWorkerExit(w, completedAbruptly)

线程执行完毕后,会在finally方法里执行processWorkerExit(w, completedAbruptly)方法,此方法会把当前线程重新添加到worker队列,所以线程可以复用

源码分析如下:

1、如果当前线程未超过核心线程数,则task不为空,直接执行task.run()方法:具体请看execute和processWorkerExit方法里的区别:

三 核心线程如何一直保持,最大线程数如何释放

请关注runWorker方法里有个getTask()方法:

1、当前线程数小于核心线程数时:timed为false,当前线程一直阻塞,所以核心线程可以一直保持,知道等待有请求加入队列才唤醒执行

2、当前线程数大于核心线程数:timed为true,workQueue.poll()超时后,进入下次循环,直接return退款while,当前线程释放,即最大线程释放

四 线上线程池大小如何设置

1、IO密集型: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

2、CPU密集型:最佳线程数目 =CPU数目+1

可以得出一个结论: 
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。 

原文地址:https://www.cnblogs.com/housh/p/13069761.html