如何创建线程池

  • 创建线程池一般有两种方法:
    • 使用ThreadPoolExecutor创建线程池(推荐)
    • 使用Executors工具类创建线程池(不推荐用于生产环境)
      • 不推荐的原因是使用Executors创建的线程池使用的阻塞队列或同步队列都可以看做是无界的,可能会导致OOM

1. 线程池简介

  • 池化技术主要是为了减少每次获取资源的消耗,提高资源利用率
  • 线程池提供了一种限制和管理资源的方式,每个线程池还维护一些基本统计信息
  • 线程池的好处:
    • 降低资源消耗
    • 提高响应速度
    • 提高线程管理的可管理性

2. Java中创建线程池的方式

  • Java中创建线程池主要有两种方式:
    • 使用ThreadPoolExecutor构造函数直接创建(推荐)
    • 使用Executors工具类创建(不推荐用于生产环境)

2.1 使用ThreadPoolExecutor构造函数创建线程池

  • 推荐这种方式,因为它允许开发者明确指定线程池的核心参数,对线程池的运行行为有更精细的控制

2.2 使用Executors工具类创建线程池

  • 使用这种方式可以创建多种类型的线程池:
    • FixedThreadPool:固定线程数量的线程池
    • SingleThreadExecutor:只有一个线程的线程池
    • CachedThreadPool:根据需要创建新线程的线程池。若所有线程都在工作且又有新任务提交,则会创建新的线程,线程执行任务完成后会返回线程池进行复用
    • ScheduledThreadPool:给定的延迟后运行任务或定期执行任务的线程池

3. 为什么不推荐使用Executors创建线程池

  • 简而言之:使用Executors创建的线程池可能会导致OOM(OutOfMemoryError)

  • FixedThreadPool和SingleThreadExecutor:使用的是阻塞队列LinkedBlockingQueue,允许创建的线程数量为Integer.MAX_VALUE,可以看做是无界的,可能堆积大量请求,从而导致OOM(OutOfMemoryError)

  • CachedThreadPool:使用的是同步队列SynchronousQueue,允许创建的线程数量为Integer.MAX_VALUE,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致OOM

  • ScheduledThreadPoolSingleThreadScheduledExecutor:使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static ExecutorService newFixedThreadPool(int nThreads) {
// LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());

}

public static ExecutorService newSingleThreadExecutor() {
// LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));

}

// 同步队列 SynchronousQueue,没有容量,最大线程数是 Integer.MAX_VALUE`
public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

}

// DelayedWorkQueue(延迟阻塞队列)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}