参考路线:https://blog.csdn.net/pfnie/article/details/52757002
首先明确一点:
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为
Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
2)CachedThreadPool 和 ScheduledThreadPool : 允 许 的 创 建 线 程 数 量 为
Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
先来看看execute能创建的4中线程池 通过工厂方法给我们提供的线程池
1.定长线程池 未设置队列长度
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2.单线程池
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。
此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
3.缓存线程池 未设置线程数量 最大线程数量为maxvalue 创建太多oom
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
4.任务调度线程池 最大线程数量为maxvalue 创建太多oom
可以调度命令在一个给定的延迟后运行,或周期性地执行。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
接下来看看ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize与maximumPoolSize 核心线程数和最大线程数
由于ThreadPoolExecutor是根据core和max 设置的边界值自动调整池大小
当执行 execute(java.lang.Runnable) 方法提交新任务时:
1.。如果运行的线程少于corePoolSize,则创建新的线程来处理请求,其他辅助线程是空闲的
2.。如果二者设置的是相同的,则创建的线程池大小是固定的,如果运行的线程与core核心线程数相同,当新的请求过来的时候若workqueue任务阻塞队列未满,则将请求放入workqueue中,等待有空闲的线程从workqueue中取出任务并处理
3.。如果运行的线程多余core少于max ,则仅仅当workqueue任务阻塞队列满时才去创建新线程去处理请求
4.。如果运行的线程多于core并且等于max,若工作队列满了,则通过handle所指定的策略去处理新的请求
5.。如果将max设置为基本的无界值 如Integer.MAX_VALUE,则允许池适应任意数量的并发任务
任务处理的优先级为:
1.。核心线程数>阻塞队列>最大线程数 如果三者都满了 使用handle处理被拒绝的任务
2.。当线程池中的线程数大于core数的时候,多余的线程就会等待keepAliveTime长的时间 如果没有请求处理,就自行销毁。
corePoolSize
在创建线程池以后 默认情况下 线程池中并没有任何线程,而是等待任务来之后才会去创建线程去处理请求 创建线程池后默认线程为0,任务来了之后会创建一个线程去执行任务,当池中的数目达到core之后,就会把任务放到阻塞队列中
maximumpoolsize
一表示最大的线程数量
workQueue
线程池所使用的任务阻塞队列,改阻塞队列的长度决定了能够缓冲任务的最大数量,阻塞队列有三种选择
ArrayBlockingQueue; 有界队列 用的少
LinkedBlockingQueue; 无界队列 一般用此队列
SynchronousQueue; 特殊的一个队列 只有存在等待取出的线程时才能加入队列,可以说是容量为0 是无界队列
keepAliveTime
表示线程没有任务执行时最多保持多久会终止
默认情况下只有当线程池中的线程数大于核心线程数时 keepaliveTime才会起作用
Timeunit时间单位参数 有7种取值 (天 小时 分钟 秒 毫秒 微秒 纳秒)
添加任务的处理流程:
当一个任务通过execute(runnable)方法欲添加到线程池时:
1.。如果当前线程池中的数量少于 core时,并且池处于running状态,那么创建并添加任务
2.。若当前=core 池处于running状态 并且队列没有满,那么任务被放入缓冲队列等待任务调度执行
3.。若大于core 队列满 小于最大线程数 那么新提交的任务会创建新的线程执行
4..若大于core 队列满 并且 线程池中的数量等于最大数量,新提交的数量会由handle来处理
5.。若大于core 多余线程空闲时间超过keepaliveTime时 会关闭这部分线程
工作线程数是不是设置的越大越好?
回答:肯定不是的
1)一来服务器CPU核数有限,同时并发的线程数是有限的,1核CPU设置10000个工作线程没有意义
2)线程切换是有开销的,如果线程切换过于频繁,反而会使性能降低