Java 中的线程池
- 什么是线程池
- 线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制,它是将多个线程预先存储在一个池子内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,只需要从池子内取出相应的线程执行对应的任务即可
- 线程池的创建方法总共有 7 种,但总体来说可分为 2 类:
- 一类是通过 ThreadPoolExecutor 创建的线程池
- 另一个类是通过 Executors 创建的线程池 ( [Java开发手册] 规范不允许使用)
- 线程池的创建方式总共包含以下 7 种(其中 6 种是通过 Executors 创建的,1 种是通过 ThreadPoolExecutor 创建的)
- Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待
- Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程
- Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序
- Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池
- Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池
- Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定,,JDK 1.8 添加
- ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置,后面会详细讲
- 单线程池的意义从以上代码可以看出 newSingleThreadExecutor 和 newSingleThreadScheduledExecutor 创建的都是单线程池,那么单线程池的意义是什么
- 虽然是单线程池,但提供了工作队列,生命周期管理,工作线程维护等功能
- ThreadPoolExecutor 参数介绍
-
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 省略…
} -
corePoolSize:核心线程数,线程池中始终存活的线程数
-
maximumPoolSize:最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数
-
keepAliveTime:最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程
-
unit: 单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:
- TimeUnit.DAYS:天
- TimeUnit.HOURS:小时
- TimeUnit.MINUTES:分
- TimeUnit.SECONDS:秒
- TimeUnit.MILLISECONDS:毫秒
- TimeUnit.MICROSECONDS:微妙
- TimeUnit.NANOSECONDS:纳秒
-
workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列
- SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列,与 SynchronousQueue 类似,还含有非阻塞方法
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 有关
-
threadFactory:线程工厂,主要用来创建线程,默认为正常优先级、非守护线程
-
handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何拒绝,系统提供了 4 种可选:
- AbortPolicy:拒绝并抛出异常
- CallerRunsPolicy:使用当前调用的线程来执行此任务
- DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务
- DiscardPolicy:忽略并抛弃当前任务
默认策略为 AbortPolicy
-
- ThreadPoolExecutor 关键节点的执行流程如下:
Java 中线程池状态
- 线程池总共存在 5 种状态,定义在 ThreadPoolExecutor 类中,代码如下
public class ThreadPoolExecutor extends AbstractExecutorService {
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
}
- 线程池的5种状态具体如下
- RUNNING:线程池创建之后的初始状态,这种状态下可以执行任务
- SHUTDOWN:该状态下线程池不再接受新任务,但是会将工作队列中的任务执行完毕
- STOP:该状态下线程池不再接受新任务,也不会处理工作队列中的剩余任务,并且将会中断所有工作线程
- TIDYING:该状态下所有任务都已终止或者处理完成,将会执行 terminated() 钩子方法
- TERMINATED:执行完 terminated() 钩子方法之后的状态。terminated 钩子方法在 Executor 终止时调用,默认实现不执行任何操作
- 线程池的状态转换规则为:
- 线程池创建之后状态为 RUNNING
- 执行线程池的 shutdown() 实例方法,会使线程池状态从 RUNNING 转变为 SHUTDOWN
- 执行线程池的 shutdownNow() 实例方法,会使线程池状态从 RUNNING 转变为 STOP
- 当线程池处于 SHUTDOWN 状态时,执行其 shutdownNow() 方法会将其状态转变为 STOP
- 等待线程池的所有工作线程停止,工作队列清空之后,线程池状态会从 STOP 转变为 TIDYING
- 执行完 terminated() 钩子方法之后,线程池状态从 TIDYING 转变为 TERMINATED
本文深入探讨了Java线程池的概念、创建方法及其内部机制。介绍了线程池的7种创建方式,包括固定大小、缓存及单线程池等,并详细解析了ThreadPoolExecutor的参数配置及线程池的5种状态。
2195

被折叠的 条评论
为什么被折叠?



