前言
使用过线程池的我们都知道,创建线程池尽量使用 ThreadPoolExecutor
;
小提一句,在springboot中推荐使用 ThreadPoolTaskExecutor
,该方法是对 ThreadPoolExecutor
进行了再一次封装;

那么为什么要使用 ThreadPoolExecutor
创建线程池呢?
在此之前,我们先了解下 Executors
创建线程池的特点;
固定数目线程的线程池:newFixedThreadPool
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
- 核心线程数和最大线程数大小一样
- keepAliveTime为0L,代表多余的线程立刻终止
- 阻塞队列为无界队列LinkedBlockingQueue
工作流程

注意:使用无界队列的线程池会导致内存飙升,因为newFixedThreadPool使用了无界的阻塞队列LinkedBlockingQueue,如果线程获取一个任务后,任务的执行时间比较长,会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终导致OOM;
使用场景
newFixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务;
可缓存线程的线程池:newCachedThreadPool
源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:
- 核心线程数为0
- 全部都是非核心线程,最大线程数为Integer.MAX_VALUE
- 阻塞队列是SynchronousQueue(最多只能存在一个元素,有新的任务则阻塞等待)
- 非核心线程空闲存活时间为60秒
工作流程

使用场景
用于并发执行大量短期的小任务;
单线程的线程池:newSingleThreadExecutor
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
特点:
- 核心线程数,最大线程数均为1,也就是每次只能有一个线程存活
- 阻塞队列是LinkedBlockingQueue(先进先出原则,所以保证了任务的按顺序逐一进行)
- keepAliveTime为0
工作流程

使用场景
适用于串行执行任务的场景,一个任务一个任务地执行;
定时及周期执行的线程池:newScheduledThreadPool
源码:
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
特点:
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是DelayedWorkQueue
- keepAliveTime为0
- scheduleAtFixedRate() :按某种速率周期执行
- scheduleWithFixedDelay():在某个延迟后执行
工作流程

使用场景
定时和周期性任务场景,需要限制线程数量的场景;
总结
这几种常用的线程池创建,在参数设置上,各有利弊,并不一定适合自己当前的系统,弊端的话,阿里的java规范已经很清楚的告诉我们了;
同时使用以上的创建方式是不够灵活的,所以我们在使用线程时尽量使用ThreadPoolExecutor
去创建线程池,按照自己系统的配置去合理设置。
网友评论