1. Java 线程池原理
线程池是一种池化技术,用于预先创建并管理一组线程,避免频繁创建和销毁线程的开销,提高性能和响应速度。 它几个关键的配置包括:核心线程数、最大线程数、空闲存活时间、工作队列、拒绝策略。
主要工作原理如下:
- 默认情况下线程不会预创建,任务提交之后才会创建线程(不过设置
prestartAllCoreThreads可以预创建核心线程)。 - 当核心线程满了之后不会新建线程,而是把任务堆积到工作队列中。
- 如果工作队列放不下了,然后才会新增线程,直至达到最大线程数。
- 如果工作队列满了,然后也已经达到最大线程数了,这时候来任务会执行拒绝策略。
- 如果线程空闲时间超过空闲存活时间,并且当前线程数大于核心线程数的则会销毁线程,直到线程数等于核心线程数(设置
allowCoreThreadTimeOut为true可以回收核心线程,默认为false)。
1. 图解过程
Section titled “1. 图解过程”任务提交,线程池中线程总数还未达到核心线程数;
核心线程数已满,任务队列尚未满;
核心线程数已满,且任务队列已满;
线程池中线程已达到最大线程数,且任务队列已满。
2. 预创建核心线程
Section titled “2. 预创建核心线程”在 ThreadPoolExecutor 里,默认情况下核心线程是懒加载的,只有在提交任务时才会创建。而 prestartAllCoreThreads() 的作用就是一次性启动线程池里的所有核心线程,让它们提前就绪,等待任务。
prestartCoreThread()启动 一个 核心线程(如果还有没创建的核心线程)。prestartAllCoreThreads()启动 所有 核心线程,返回实际启动的线程数。
ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 默认情况下还没有线程被创建System.out.println("初始线程数: " + executor.getPoolSize()); // 0
// 预启动所有核心线程int started = executor.prestartAllCoreThreads();System.out.println("当前线程数: " + executor.getPoolSize()); // 53. 处理线程退出(processWorkerExit)
Section titled “3. 处理线程退出(processWorkerExit)”processWorkerExit 的作用是在线程池中,当一个工作线程正常完成任务、空闲超时被回收或非正常退出时,负责清理该线程(从线程池集合移除、更新线程计数)、检查线程池状态,并在必要时补充新线程以保证线程池能够持续处理队列中的任务。
当一个线程完成任务或者被中断/空闲超时退出时,processWorkerExit 会负责:
-
减少线程池中当前线程计数
workerCount会原子减一,保证线程池统计准确。 -
从线程集合中移除 Worker
workers.remove(worker),避免池里保存已经退出的线程对象。 -
判断是否需要创建新的线程
如果线程池仍在运行状态,并且任务队列里还有待处理任务,可能创建新的线程去处理队列中的任务,保证线程池维持一定数量线程。
-
更新最大线程数统计
largestPoolSize用于监控线程池历史峰值。 -
通知等待线程/唤醒线程池管理器
让其它线程知道有线程退出,有可能触发重新创建线程或执行拒绝策略。
4. 工作队列类型
Section titled “4. 工作队列类型”| 队列类型 | 队列容量 | 核心线程满了怎么办 | 适合场景 |
|---|---|---|---|
LinkedBlockingQueue | 可以无限放 | 任务直接排队等线程空闲执行 | CPU 密集型、任务量可控 |
ArrayBlockingQueue | 有上限 | 队列满了就创建非核心线程 | 控制线程数量、防止内存爆掉 |
SynchronousQueue | 不存储任务 | 每个任务必须直接交给线程执行 | 高并发短任务、尽量不缓存 |
DelayQueue / PriorityBlockingQueue | 有上限或按优先级控制 | 按延迟时间或优先级顺序取任务 | 定时任务或优先级任务 |