为什么不建议用Executors创建线程池
在Java中,java.util.concurrent.Executors
类提供了一些工厂方法来创建线程池,如newFixedThreadPool
、newCachedThreadPool
等。虽然这些方法提供了快速简便的方式来创建线程池,但它们并不总是推荐使用的最佳实践。主要原因是这些工厂方法通常会使用默认设置创建线程池,这些设置在某些情况下可能导致资源不当使用,甚至引起应用程序性能问题。
以下是不建议使用Executors
类方法创建线程池的主要原因:
1. newFixedThreadPool
和newSingleThreadExecutor
:
这两个工厂方法创建的线程池有无界的工作队列(LinkedBlockingQueue
),这意味着如果任务提交速度超过线程池处理速度,队列会无限增长,最终可能会导致内存耗尽。
示例代码:
ExecutorService executorService = Executors.newFixedThreadPool(10);
源码分析(newFixedThreadPool
):
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
在上面的代码中,newFixedThreadPool
会创建一个ThreadPoolExecutor
,并使用无界的LinkedBlockingQueue
作为工作队列。
2. newCachedThreadPool
和newScheduledThreadPool
:
newCachedThreadPool
创建了一个线程池,其中线程数理论上可以无限增长。当新任务提交到线程池时,如果线程池中所有线程都在执行任务,则会创建一个新的线程,而不是排队。这可能会导致创建大量线程,进而耗尽系统资源。
示例代码:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源码分析(newCachedThreadPool
):
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
在上面的代码中,newCachedThreadPool
使用SynchronousQueue
作为工作队列,并允许线程池中线程数量达到Integer.MAX_VALUE
(实际上是无界的)。
3. 默认拒绝策略:
Executors
类中的线程池默认使用AbortPolicy
作为拒绝策略,当任务无法提交给线程池时,会抛出RejectedExecutionException
异常。这可能在高负载下导致程序崩溃。
最佳实践:
使用ThreadPoolExecutor
的构造函数直接创建线程池,并提供合理的参数,以便于更好地控制线程池行为,避免资源耗尽等问题。
示例代码:
int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
ExecutorService pool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
在上面的代码中,我们创建了一个ThreadPoolExecutor
并对其进行了手动配置。我们设置了核心线程数、最大线程数、保持活动时间、时间单位、工作队列、线程工厂和拒绝策略。这样我们就可以根据实际业务需求对线程池进行细粒度的配置。
总结来说,Executors
类的工厂方法虽然方便,但是由于其默认的一些设定可能不适合所有场景,因此在生产环境中,通常建议直接使用ThreadPoolExecutor
的构造函数来创建线程池,并根据实际需求进行细节上的调整。这样做可以防止资源耗尽,并且可以更灵活地根据负载情况调整线程池策略。
还没有评论,来说两句吧...