Executors是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
方法名称 | 说明 |
---|---|
public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
public static ExecutorService newSingleThreadExecutor() | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉, |
public static ScheduledExecutorService newscheduledThreadPool(int corePoolsize) | 创建一个线程池,可以实现在给定的延迟后运行任务或者定期执行任务。 |
注意:这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
Executors使用可能存在的陷阱
大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
阿里巴巴Java开发手册
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool和singleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(内存溢出)。
2)CachedThreadPool和ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
线程池的核心线程和最大连接的设置
配置线程池的核心线程数量(corePoolSize
)和最大线程数量(maximumPoolSize
)时,没有固定的公式,但可以根据具体的应用场景和系统资源进行合理设置。
以下是一些建议:
-
核心线程数 (
corePoolSize
):- 对于CPU密集型任务,可以将核心线程数设置为
CPU 核心数 + 1
,以充分利用 CPU 资源。 - 对于IO密集型任务,可以将核心线程数设置得更高,因为 IO 操作会阻塞线程,更多的线程可以提高并发处理能力。
- 对于CPU密集型任务,可以将核心线程数设置为
-
最大线程数 (
maximumPoolSize
):- 最大线程数通常设置为核心线程数加上一个合理的额外线程数,以应对突发的高负载情况。
- 一般情况下,最大线程数可以设置为
2 * CPU 核心数
或更高,具体取决于系统的内存和 CPU 资源。
-
队列大小 (
workQueue
):- 队列大小也会影响线程池的性能。如果队列大小设置得过大,可能会导致大量任务积压,占用大量内存。
- 如果队列大小设置得过小,可能会导致频繁创建新线程,增加系统开销。
综合考虑以上因素,可以参考以下公式进行配置:
int cpuCores = Runtime.getRuntime().availableProcessors();
int corePoolSize = cpuCores + 1; // CPU 密集型任务
// int corePoolSize = 2 * cpuCores; // IO 密集型任务
int maximumPoolSize = 2 * cpuCores;
当然,上述这些只是建议值,实际应用中需要根据具体的业务需求和系统资源进行调整。
其他常见问题
Executors工具类底层是基于什么方式实现的线程池对象?
线程池ExecutorService的实现类:ThreadPoolExecutor
Executors是否适合做大型互联网场景的线程池方案?
不合适。建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险