文章目录
十二、连接池线程池配置——线程池
线程池是为了提高系统性能而设计的,通过减少线程的创建和销毁次数,降低了系统资源的消耗。线程池能够有效地限制并发线程的数量,防止因线程过多而导致的系统崩溃。
1. 线程池的基本概念
每个线程都需要一个内存栈来存储局部变量和操作栈等信息。可以通过 -Xss
参数调整每个线程的栈大小(64位系统默认 1024KB,建议根据实际需求调整,如 256KB),以便创建更多的线程。
然而,JVM 并不会无限制地创建线程,因此使用线程池可以限制线程的数量,从而保护系统的稳定性。线程池通常配合队列使用,以限制并发处理任务的数量。当任务数量超过队列容量时,可以通过拒绝策略来处理,从而保护系统免受过多请求的冲击。
2. 线程池的主要配置参数
2.1 主要配置参数
-
corePoolSize:核心线程池大小,指在没有任务处理时线程池中保持的最小线程数。即使线程池中的任务已经完成,核心线程也会被保留,以便快速处理未来的任务。
-
maximumPoolSize:线程池最大线程数,指在任务量非常大时线程池中允许创建的最大线程数量。当任务数超过核心线程数且任务队列满时,线程池将创建新的线程,直到达到最大线程数。
-
keepAliveTime:线程池中线程的最大空闲时间。超过此时间的线程会被回收,以释放资源。如果线程池中的线程数超过核心线程数且线程处于空闲状态超过
keepAliveTime
,这些线程将被终止,线程池会缩小到核心线程数大小。 -
workQueue:线程池使用的任务缓冲队列,用于存放等待执行的任务。常见类型包括:
- 有界阻塞数组队列
ArrayBlockingQueue
:固定大小的队列,任务满时会阻塞添加任务的线程。 - 有界/无界阻塞链表队列
LinkedBlockingQueue
:链表结构的队列,支持有界和无界,通常用于生产者-消费者场景。 - 优先级阻塞队列
PriorityBlockingQueue
:支持任务优先级的队列,任务按优先级顺序处理。 - 无缓冲区阻塞队列
SynchronousQueue
:不存储任务的队列,提交任务时需要有线程立即处理,常用于任务立即执行的场景。
- 有界阻塞数组队列
-
threadFactory:用于创建线程的工厂,可以自定义线程的名称、是否为后台线程等属性。通过实现
ThreadFactory
接口,可以创建具有特定属性的线程。 -
rejectedExecutionHandler:当任务队列满且线程池的线程数已达到最大线程数时,线程池的拒绝策略用于处理无法执行的任务。包括:
AbortPolicy
:直接抛出RejectedExecutionException
异常,表明任务无法被接受。DiscardPolicy
:丢弃无法执行的任务,不抛出异常。DiscardOldestPolicy
:丢弃最旧的任务,将新的任务添加到队列中。CallerRunsPolicy
:由主线程执行无法被接受的任务,减少任务提交速率。
2.2 主要配置参数示例
下面是一个基于 ThreadPoolExecutor
的线程池配置示例,演示如何设置主要的线程池参数:
import java.util.concurrent.*;
public class ThreadPoolConfig {
public static ExecutorService createThreadPool() {
// 创建线程池
return new ThreadPoolExecutor(
10, // corePoolSize: 核心线程池大小
50, // maximumPoolSize: 线程池最大线程数
60L, // keepAliveTime: 线程的最大空闲时间
TimeUnit.SECONDS, // keepAliveTime 的时间单位
new LinkedBlockingQueue<>(100), // workQueue: 任务队列,使用有界阻塞链表队列
new ThreadFactory() {
// threadFactory: 创建线程的工厂
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "CustomThread"); // 设置线程名称
}
},
new ThreadPoolExecutor.AbortPolicy() // rejectedExecutionHandler: 拒绝策略,直接抛出异常
);
}
public static void main(String[] args) {
ExecutorService executorService = createThreadPool();
// 提交测试任务
for (int i = 0; i < 120; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is being executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务处理
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executorService.shutdown();
}
}
运行示例:
配置说明:
-
corePoolSize:设置为
10
,表示线程池中至少保留 10 个核心线程,即使这些线程没有任务时也不会被回收。 -
maximumPoolSize:设置为
50
,表示线程池中允许最多创建 50 个线程来处理任务。 -
keepAliveTime:设置为
60L
,表示线程空闲时间达到 60 秒后将被回收。时间单位为TimeUnit.SECONDS
。 -
workQueue:使用
LinkedBlockingQueue
类型的任务队列,大小设置为100
。如果线程池中的线程数量达到核心线程数,任务将被放入队列中等待执行。 -
threadFactory:自定义
ThreadFactory
,创建线程时设置线程名称为"CustomThread"
。可以帮助更好地识别线程。 -
rejectedExecutionHandler:使用
AbortPolicy
拒绝策略。当任务队列满且线程池中线程数已达到最大线程数时,直接抛出RejectedExecutionException
异常。
3. 线程池的实现
Java 提供了 ExecutorService
的几种主要实现,用于满足不同的任务处理需求:
- ThreadPoolExecutor:标准线程池实现,提供了高度的配置灵活性。
- ScheduledThreadPoolExecutor:支持延迟和定期执行任务的线程池。
- ForkJoinPool:使用 work-stealing 模式的线程池,适用于需要处理大量小任务的场景。
3.1 创建线程池
不同类型的线程池可以通过 Executors
工厂方法来创建,以下是常用的几种线程池及其等价实现方式:
-
单线程池
单线程池始终使用一个工作线程来执行任务,保证任务按顺序执行。
ExecutorService executorService = Executors.newSingleThreadExecutor();
等价于:
return new FinalizableDelegatedExecutorService( new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );
-
固定数量线程池
固定数量线程池始终使用固定数量的线程来执行任务,适用于任务负载固定的情况。
ExecutorService executorService = Executors.newFixedThreadPool(10);
等价于:
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
-
可缓存线程池
可缓存线程池会根据需要创建新线程,并缓存空闲线程,适用于任务量动态变化的情况。
ExecutorService executorService = Executors.newCachedThreadPool();
等价于:
return new ThreadPoolExecutor