线程池类型场景和问题
https://mp.weixin.qq.com/s/Cv5gTiz9RORnesoQmyROIw
Executors线程工厂类
1、Executors.newCachedThreadPool();
说明:
创建的线程池核心线程0 , 最大线程是Integer.MaxValue。 线程空闲存活时间1分钟。 默认异常拒绝策略,使用SynchronousQueue队
特点:
每次添加任务如果没有空闲线程就会新建一个线程去执行。
SynchronousQueue是阻塞队列,加入任务的线程会阻塞住,直到其它线程从中取走任务才会结束阻塞
线程创建上限近乎无限
队列:
SynchronousQueue
适用场景:
所以它适用于任务加入比较稳当且加入间隔短的场景
实现:
new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());
问题:
可能在大量请求的时候,创建了过多的线程,导致的效率低下,甚至OOM。
2、Executors.newFixedThreadPool(int);
说明:
核心线程和最大线程数是你传入的参数。 其他参数和 Executors.newSingleThreadExecutor一样
实现:
new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
阻塞队列:
无界的 LinkedBlockingQueue 阻塞队列
问题:
也是有可能造成队列积压,以至于导致OOM的问题,所以,我们在使用时要合理的设置线程数。
newFixedThreadPool线程池的核心线程数是固定的,它使用了近乎于无界的 LinkedBlockingQueue 阻塞队列。(平安问过)
当核心线程用完后,任务会入队到阻塞队列,如果任务执行的时间比较长,没有释放,会导致越来越多的任务堆积到阻塞队列,最后导致机器的内存使用不停的飙升,造成JVM OOM。
(参考: https://www.cnblogs.com/hulianwangjiagoushi/p/11498353.html)
3、Executors.newSingleThreadExecutor();
说明:
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。
特点:
只有一个线程
近乎可以接收无限任务的队列, 可以堆积大量任务
适用于任务持续加入但是任务数并不多的场景
实现:
new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())
问题:
如果有其他请求任务时,此时线程没有空闲,就会将此任务放到队列中等待执行。
4、Executors.newScheduledThreadPool(int);
说明:
创建一个定长线程池,支持定时及周期性任务执行。
特点:
核心线程是传入的参数,最大线程是int上线, 默认存活时间是10毫秒, 任务队列使用自己实现的DelayedWorkQueue, 拒绝策略异常策略
加入任务的时候,会把任务和定时时间构建一个RunnableScheduledFuture对象,再把这个对象放入DelayedWorkQueue队列中,
DelayedWorkQueue是一个有序队列, 他会根据内部的RunnableScheduledFuture的运行时间排序内部对象。
任务加入后就会启动一个线程。 这个线程会从DelayedWorkQueue中获取一个任务。
阻塞队列:
DelayedWorkQueue内部是按照时间从前完后获取任务的。如果任务的中的时间还没有到。 获取的就是null。
获取任务结束,线程会休眠10毫秒。所以这个定时任务的执行最小间隔是10毫秒的。
内部实现:
new ScheduledThreadPoolExecutor(corePoolSize)
问题:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。