本节解析要点如下:
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 }
核心要点如下:
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时间所占比例越高,需要越少线程。