概述
重新学习一下线程池
参考文章:
https://www.jianshu.com/p/0e4a5e70bf0e
简介
工作原理
核心参数
线程池中有6个核心参数,如下:
- 上述6个参数的配置 决定了 线程池的功能,具体设置时机 —-> 创建 线程池类对象时 传入
ThreadPoolExecutor
类 = 线程池的真正实现类- 开发者可根据不同需求 配置核心参数,从而实现自定义线程池
1 | // 创建线程池对象如下 |
同时,Java
里已内置4种常用的线程池(即 已经配置好核心参数),下面慢慢学。
工作逻辑
使用
流程如下:
1 | // 1. 创建线程池 |
shutdown()
线程池的状态则立刻变成SHUTDOWN状态
不能接受新的submit(即不能再往线程池内添加任何任务,否则将抛出异常)
- 并没有任何的interrupt操作,会等待线程池中所有线程(执行中的以及排队的)执行完毕
- 可以理解为是个标识性质的方法,标识这程序有意愿在此刻终止线程池的后续操作。
shutdownNow()
线程池的状态立刻变成STOP状态
会尝试interrupt线程池中正在执行的线程
- 等待执行的线程也会被取消
- 但是并不能保证一定能成功的interrupt线程池中的线程。(因为它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。)
- 会返回并未终止的线程列表List
shutdownNow()方法比shutdown()强硬了很多,不仅取消了排队的线程而且确实尝试终止当前正在执行的线程。
常见的4类功能线程池
根据参数的不同配置,Java
中最常见的线程池有4类:
- 定长线程池(
FixedThreadPool
) - 定时线程池(
ScheduledThreadPool
) - 可缓存线程池(
CachedThreadPool
) - 单线程化线程池(
SingleThreadExecutor
)
即 对于上述4类线程池,
Java
已根据 应用场景 配置好核心参数
定长线程池(FixedThreadPool)
特点:只有核心线程 & 不会被回收、线程数量固定、任务队列无大小限制(超出的线程任务会在队列中等待)
应用场景:控制线程最大并发数
具体使用:通过 Executors.newFixedThreadPool() 创建
示例:
1 | // 1. 创建定长线程池对象 & 设置线程池线程数量固定为3 |
定时线程池(ScheduledThreadPool )
- 特点:核心线程数量固定、非核心线程数量无限制(闲置时马上回收)
- 应用场景:执行定时 / 周期性 任务
- 使用:通过Executors.newScheduledThreadPool()创建
- 示例:
1 | // 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5 |
可缓存线程池(CachedThreadPool)
- 特点:只有非核心线程、线程数量不固定(可无限大)、灵活回收空闲线程(具备超时机制,全部回收时几乎不占系统资源)、新建线程(无线程可用时)
- 任何线程任务到来都会立刻执行,不需要等待
- 应用场景:执行大量、耗时少的线程任务
- 使用:通过Executors.newCachedThreadPool()创建
- 示例:
1 | // 1. 创建可缓存线程池对象 |
单线程化线程池(SingleThreadExecutor)
特点:只有一个核心线程(保证所有任务按照指定顺序在一个线程中执行,不需要处理线程同步的问题)
应用场景:不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作,文件操作等
使用:通过Executors.newSingleThreadExecutor()创建
示例:
1 | // 1. 创建单线程化线程池 |
总结&对比
说明
阿里的开发规范中有这么一条:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,避免资源耗尽的风险。
因此,我们可以通过ThreadPoolExecutor的方式自己创建线程池,根据业务逻辑选择阻塞队列、拒绝策略等。