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