JAVA线程池
1. 为什么要使用线程池
我们知道创建线程和销毁线程都是很损耗性能的,使用线程池可以复用线程,避免频繁的创建销毁线程,线程池主要解决两个问题:
- 执行大量异步任务时线程池能提供很好的性能
- 线程池提供一种资源限制和管理手段,通过线程复用更好的管理和协调线程工作
2.阻塞队列
开始介绍线程池之前,我们先来看下Java常见阻塞队列。
- ArrayBlockingQueue: 数组结构的有界阻塞队列 , FIFO原则对元素进行排序。
- LinkedBlockingQueue: 链表结构的有序阻塞队列 , FIFO原则对元素进行排序。内部维持一个数据缓冲队列。
- PriorityBlockingQueue: 支持优先级排序的无界阻塞队列默认采取升序排序。
- DelayQueue: 使用优先级队列实现的误解阻塞队列,队列中的元素必须实现Delay接口,指定到期时间,元素到期后才能从队列中取走。
- SycbronousQueue: 不存储元素的阻塞队列。
- LinkedTransferQueue: 链表组成的无界阻塞队列。
- LinkedBlockingDeque: 链表组成的双向阻塞队列。可从队列两端插入和移除元素。
3.线程池种类
java中常用的线程池有四种
-
newSingleThreadExecutor
单线程的线程池,保证所有任务按先进先出的顺序执行
-
newCachedThreadPool
创建一个可缓存的线程池,可灵活回收空闲线程
-
newFixedThreadPool
创建拥有固定线程的线程池
-
newScheduledThreadPool
创建定时线程池,支持定时及周期性任务执行
3.1 SingleThreadExecutor
源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心线程数和最大线程数均为1,任务队列使用无界阻塞队列LinkedBlockingQueue。
执行示意图
注意
由于使用的是无界阻塞队列,理论上可以提交无限个任务,当核心线程执行任务的时间很长,不断提交任务容易导致oom
3.2 CahcedThreadPool
源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
coreThread参数为0 ,说明只存在非核心线程,并且最大线程数为无限,空闲时间为60s。任务队列是不存储元素的SynchronousQueue,因此没个任务的插入\移除操作都要等待另一个任务的移除\插入操作。
工作示意图
3.3 FixedThreadPool
源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看出FixedThreadPool只有核心线程,且数量是固定的
工作示意图
3.4 ScheduledThreadPool
源码
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
corePoolSize为传进来的固定值,因为采用DelayedWorkQueue,因此maximumPoolSize设置Integer.MAX_VALUE是无效的。
工作示意图
4.线程池构造函数
通过ThreadPoolExecutor创建线程池。其构造方法:
/*
* corePoolSize: 核心线程数
* maximumPoolSize: 允许创建的最大线程数
* keepAliveTime: 非核心线程闲置超时时间
* TimeUnit: 超时时间单位
* workQueue: 阻塞队列
* ThreadFactory: 线程工厂
* RejectExecutionHandler: 饱和策略
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
注: 饱和策略默认为AbordPolicy: 抛出RejectedExecutionException。
其他还有
CallerRunPolicy: 用调用者所在线程来处理任务。
DiscardPolicy: 不能执行任务,并将改任务删除。
DiscardOldestPolicy: 丢弃队列最近的任务,并执行当前任务。