线程池
什么是线程池
线程池(Thread Pool)是一种基于池化思想管理线程的工具,主要用于减少创建和销毁线程的开销。在多线程编程中,频繁地创建和销毁线程会消耗大量系统资源,而线程池可以复用一组已经创建好的线程。
为什么要使用线程池
线程池是多线程编程中常用的一种优化手段,可以提高资源利用率,提升系统性能,并降低系统的复杂性。
这里借用《Java 并发编程的艺术》提到的来说一下使用线程池的好处:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
创建线程池
可以通过java.util.concurrent
包中的Executors
工厂类来创建线程池。
我们可以创建多种类型的 ThreadPoolExecutor
:
FixedThreadPool
:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。SingleThreadExecutor
: 该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。CachedThreadPool
: 该方法返回一个可根据实际情况调整线程数量的线程池。初始大小为 0。当有新任务提交时,如果当前线程池中没有线程可用,它会创建一个新的线程来处理该任务。如果在一段时间内(默认为 60 秒)没有新任务提交,核心线程会超时并被销毁,从而缩小线程池的大小。ScheduledThreadPool
:该方法返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池。
对应 Executors
工厂类中的方法如图所示:
为什么不推荐使用内置线程池
详见 阿里巴巴Java开发手册 中的 (七)并发处理
-
FixedThreadPool
和SingleThreadExecutor
:使用的是无界的LinkedBlockingQueue
,任务队列最大长度为Integer.MAX_VALUE
,可能堆积大量的请求,从而导致 OOM。 -
CachedThreadPool
:使用的是同步队列SynchronousQueue
, 允许创建的线程数量为Integer.MAX_VALUE
,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。 -
ScheduledThreadPool
和SingleThreadScheduledExecutor
: 使用的无界的延迟阻塞队列DelayedWorkQueue
,任务队列最大长度为Integer.MAX_VALUE
,可能堆积大量的请求,从而导致 OOM。
创建自定义线程池
可以使用ThreadPoolExecutor
类直接创建一个自定义线程池,需要的参数如下图。
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 核心线程数 - 即使空闲也会保持存活的线程的数量
int corePoolSize = 5;
// 最大线程数 - 可以同时活跃的最大线程数量
int maximumPoolSize = 10;
// 线程没有任务执行时保持存活的时间
long keepAliveTime = 120;
// keepAliveTime的时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 工作队列 - 存放待处理任务的队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(50);
// 线程工厂 - 用于创建新线程的工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 拒绝策略 - 当线程池和队列都满时如何处理新提交的任务
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 使用线程池执行任务
// 示例任务,实际应用中应替换为实际的Runnable任务
threadPool.execute(() -> {
System.out.println("任务执行了");
});
// 在应用程序结束时关闭线程池
threadPool.shutdown();
}
}
线程池的七大参数
参考上面的图片可以知道,创建线程池需要7个参数