一、线程池的主要核心参数:
-
CORE_POOL_SIZE:
-
核心线程数,设置为JVM可用的处理器核心数
-
这些线程会一直存活,即使处于空闲状态
-
-
MAX_POOL_SIZE:
-
最大线程数
-
当队列满且核心线程都在忙时,会创建新线程直到达到此上限
-
-
QUEUE_CAPACITY:
-
工作队列的容量
-
在核心线程都忙时,新任务会先进入队列而不是创建新线程
-
-
KEEP_ALIVE_TIME:
-
非核心线程的空闲存活时间,设置为1秒
-
超过核心线程数的线程在空闲超过此时间后会被终止
-
-
TimeUnit:
-
存活时间的单位
-
-
workQueue
-
使用有界队列来存储等待执行的任务
-
-
RejectedExecutionHandler:
-
CallerRunsPolicy 默认策略,直接抛出异常
-
DiscardPolicy 静默丢弃,不抛异常
-
DiscardOldestPolicy 丢弃队列最旧任务,再尝试提交新任务
-
CallerRunsPolicy 调用者线程自己执行任务
-
自定义拒绝策略
-
如果内置策略不满足需求,可以实现
RejectedExecutionHandler
接口自定义策略:ExecutorService executor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,TimeUnit.SECONDS,new LinkedBlockingQueue<>(capacity),(Runnable r, ThreadPoolExecutor executor) -> {// 自定义逻辑,如记录日志、降级处理等System.err.println("Task rejected: " + r);} );
-
-
二、四种常用的线程池
但是Java 通过 Executors
类提供了四种常用的线程池实现,每种都有其特定的使用场景和优缺点。
1. FixedThreadPool(固定大小线程池)
创建方式:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads);public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
优点:
-
线程数量固定,可以控制最大并发数
-
适用于负载较重的服务器环境
-
线程不会被回收,减少了线程创建销毁的开销
-
任务队列无界,可以接收大量任务
缺点:
-
使用无界队列(LinkedBlockingQueue),可能导致内存溢出
-
如果线程因异常终止,会创建新线程替代
-
不适合任务执行时间差异很大的场景
适用场景:
-
需要限制线程数量的场景
-
CPU密集型任务
2. CachedThreadPool(可缓存线程池)
创建方式:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
优点:
-
线程数量几乎无限制(Integer.MAX_VALUE)
-
空闲线程会被回收(默认60秒)
-
自动扩展线程池大小应对突发流量
-
使用SynchronousQueue,任务不会被排队,它是一个零容量队列(一个不存储元素的阻塞队列)。
缺点:
-
线程数量无限制可能导致系统资源耗尽
-
不适合处理执行时间较长的任务
-
大量短任务可能导致频繁创建销毁线程
适用场景:
-
大量短生命周期的异步任务
-
负载较轻的服务器
-
IO密集型任务
3. SingleThreadExecutor(单线程线程池)
创建方式:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
优点:
-
保证所有任务按提交顺序执行
-
不需要处理线程同步问题
-
线程异常终止后会创建新线程继续执行
缺点:
-
性能低下,无法利用多核CPU
-
使用无界队列,可能内存溢出
-
一个任务阻塞会影响后续所有任务
适用场景:
-
需要任务顺序执行的场景
-
不需要并发性的任务
-
日志处理等顺序性要求高的任务
4. ScheduledThreadPool(定时任务线程池)
创建方式:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(corePoolSize);public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}
优点:
-
支持定时及周期性任务执行
-
可以替代Timer类,功能更强大
-
线程数固定,不会无限增长
-
异常处理比Timer更可靠
缺点:
-
复杂定时任务逻辑实现较复杂
-
执行时间较长的任务可能影响后续任务
-
使用DelayedWorkQueue,任务过多可能内存溢出
适用场景:
-
需要定时执行的任务
-
周期性任务(如心跳检测)
-
需要延迟执行的任务
总结对比
线程池类型 | 核心线程数 | 最大线程数 | 工作队列 | 线程存活时间 | 适用场景 |
---|---|---|---|---|---|
FixedThreadPool | 固定 | 固定 | LinkedBlockingQueue | 无 | CPU密集型 |
CachedThreadPool | 0 | Integer.MAX_VALUE | SynchronousQueue | 60秒 | 短生命期IO密集型 |
SingleThreadExecutor | 1 | 1 | LinkedBlockingQueue | 无 | 顺序执行任务 |
ScheduledThreadPool | 固定 | Integer.MAX_VALUE | DelayedWorkQueue | 无 | 定时/周期性任务 |
通用缺点:
-
除CachedThreadPool外,其他三种都使用无界队列,可能导致OOM
-
默认的线程工厂创建的线程没有有意义的名称,不利于问题排查
-
默认的拒绝策略可能不符合业务需求
最佳实践:
-
对于生产环境,建议根据业务需求自定义ThreadPoolExecutor
-
为线程设置合理的名称前缀
-
根据任务类型选择合适的队列和拒绝策略