线程池-面试
线程池定义
线程池是一种线程管理机制,通过预先创建并维护一组线程,避免频繁创建和销毁线程的开销。任务提交到线程池后,由池中的线程执行,执行完毕后线程返回池中等待新任务。
线程池的优点
降低资源消耗:复用已创建的线程,减少线程创建和销毁的开销。
提高响应速度:任务到达时无需等待线程创建即可立即执行。
提高线程可管理性:统一分配、监控和调优线程资源。
避免过载风险:通过队列和拒绝策略控制并发任务数量。
核心类和接口
Executor
:顶层接口,定义执行任务的方法execute(Runnable)
。ExecutorService
:扩展Executor
,增加了任务提交、关闭管理等功能。ThreadPoolExecutor
:线程池的核心实现类。ScheduledExecutorService
:支持定时或周期性任务调度。
ThreadPoolExecutor 核心构造参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数long keepAliveTime, // 非核心线程空闲存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // 任务队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
)
1. corePoolSize (核心线程数)
定义:线程池中保持存活的最小线程数量,即使这些线程处于空闲状态。
工作方式:
当新任务提交时,如果当前线程数 小于
corePoolSize
,即使有其他工作线程空闲,线程池也会立即创建一个新的核心线程来处理该任务。核心线程默认不会因为空闲太久而被回收,除非设置了
allowCoreThreadTimeOut(true)
。
2. maximumPoolSize (最大线程数)
定义:线程池允许创建的最大线程数量。
工作方式:
当任务数量激增,导致工作队列已满时,线程池会创建新的、非核心线程来处理任务,直到线程总数达到
maximumPoolSize
。这个参数限制了线程池所能承载的并发能力的上限。
3. keepAliveTime + unit (线程存活时间)
定义:当线程池中的线程数量超过
corePoolSize
时,非核心线程在空闲状态下的存活时间。工作方式:
如果一个非核心线程在
keepAliveTime
时间内没有获取到新任务,它将被终止并回收,从而减少系统资源消耗。如果调用了
allowCoreThreadTimeOut(true)
,此策略也会应用于核心线程。
4. workQueue (工作队列)
定义:用于保存等待执行的任务的阻塞队列(
BlockingQueue<Runnable>
)。工作方式:
当线程池中的线程数达到
corePoolSize
后,新来的任务会被放入workQueue
中等待,而不是直接创建新线程。队列的选择对线程池的运行行为有重大影响。常见的有:
LinkedBlockingQueue
(无界队列):任务可以无限堆积,直到耗尽内存。maximumPoolSize
参数将失效。ArrayBlockingQueue
(有界队列):可以设置固定容量。有助于防止资源耗尽,但需要与maximumPoolSize
配合调整。SynchronousQueue
(同步移交队列):它不存储元素。每个插入操作必须等待另一个线程的移除操作。这将导致线程池总是创建新线程(除非达到maximumPoolSize
),通常要求很大的maximumPoolSize
。
5. threadFactory (线程工厂)
定义:用于创建新线程的工厂(
ThreadFactory
)。工作方式:
线程池通过调用
ThreadFactory
的newThread(Runnable r)
方法来创建所有线程。你可以自定义
ThreadFactory
,来设置线程的名称、优先级、是否为守护线程(daemon)等,便于监控和调试。例如,给线程命名为my-pool-thread-1
。
6. handler (拒绝策略处理器)
定义:当线程池和工作队列都已饱和(线程数达到
maximumPoolSize
且队列已满)时,用于处理新提交任务的策略(RejectedExecutionHandler
)。内置策略:
AbortPolicy
(默认):直接抛出RejectedExecutionException
异常。CallerRunsPolicy
:由提交任务的调用者线程(通常是主线程)自己来执行这个任务。这是一种简单的反馈机制,可以降低新任务的提交速度。DiscardPolicy
:默默丢弃无法处理的任务,不抛出任何异常。DiscardOldestPolicy
:丢弃队列中最旧的一个任务,然后尝试重新提交当前任务。
线程池的工作流程
提交任务后,若核心线程数未满,创建新线程执行任务。
核心线程已满时,任务进入阻塞队列等待。
队列满且线程数未达最大值,创建非核心线程执行任务。
线程数达到最大值且队列满,触发拒绝策略。
Executors 提供的常见线程池
newFixedThreadPool
:固定大小线程池,队列无界,可能堆积任务导致 OOM。newCachedThreadPool
:线程数无界,空闲线程超时回收,可能创建过多线程。newSingleThreadExecutor
:单线程池,队列无界。newScheduledThreadPool
:支持定时任务。
不推荐使用 Executors
的原因:
其预设的线程池参数可能导致资源耗尽(如无界队列引发 OOM),推荐手动配置 ThreadPoolExecutor
。
合理配置线程池参数
corePoolSize
:CPU 密集型任务设置为CPU 核数 + 1
;IO 密集型任务可参考CPU 核数 * 2
。workQueue
:有限队列(如ArrayBlockingQueue
)避免无界队列堆积。maximumPoolSize
:根据系统负载和任务特性调整,避免过高导致上下文切换开销。
线程池的生命周期
RUNNING:接受新任务并处理队列中的任务。
SHUTDOWN:不再接受新任务,但处理队列中的任务。
STOP:中断所有线程,丢弃队列任务。
TIDYING:所有任务终止,线程数为 0,准备执行
terminated()
钩子。TERMINATED:
terminated()
执行完毕,线程池彻底关闭。
通过 shutdown()
或 shutdownNow()
触发状态转换。