在 Java 中,线程池是一种管理和复用线程的机制,可以有效地控制线程的数量,降低线程创建和销毁的开销,避免系统资源被耗尽。Java 提供了 java.util.concurrent
包中的 ThreadPoolExecutor
来实现线程池管理。下面我们来看看如何使用线程池及其核心参数。
1. Java 线程池的创建
可以通过 ThreadPoolExecutor
类或 Executors
工具类来创建线程池。推荐使用 ThreadPoolExecutor
,因为 Executors
工具类的某些方法(如 newFixedThreadPool
和 newCachedThreadPool
)创建的线程池配置过于宽泛,可能在特殊场景下出现问题(如资源耗尽)。
创建线程池的基本代码示例:
// 自定义线程池创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(queueCapacity), // 任务队列
new ThreadFactory() { // 线程工厂(可选)
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "custom-thread");
}
},
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略(可选)
);
2. 核心参数
ThreadPoolExecutor
中的核心参数及其作用如下:
-
corePoolSize(核心线程数):
- 核心线程是线程池中始终保持存活的线程数量,即使它们处于空闲状态。除非
allowCoreThreadTimeOut
被设置为true
。 - 当任务数少于核心线程数时,线程池会优先使用核心线程来执行任务。
- 核心线程是线程池中始终保持存活的线程数量,即使它们处于空闲状态。除非
-
maximumPoolSize(最大线程数):
- 线程池中允许的最大线程数量,包括核心线程。对于高并发场景,当任务队列被占满时,线程池会创建超过核心线程数量的线程来处理任务,但不会超过最大线程数。
- 超过
maximumPoolSize
的任务会触发拒绝策略。
-
keepAliveTime(线程存活时间):
- 非核心线程的空闲存活时间,即当线程数超过核心线程数,空闲线程存活时间超过
keepAliveTime
后会被回收。 - 如果
allowCoreThreadTimeOut
被设为true
,核心线程也会应用此存活时间。
- 非核心线程的空闲存活时间,即当线程数超过核心线程数,空闲线程存活时间超过
-
unit(时间单位):
keepAliveTime
的时间单位,通常为TimeUnit.SECONDS
或TimeUnit.MILLISECONDS
。
-
workQueue(任务队列):
- 用于存储等待执行的任务。常用的队列类型包括:
ArrayBlockingQueue
:一个有界的阻塞队列,适合有界队列场景。LinkedBlockingQueue
:一个无界的链表队列,任务数过多可能导致内存溢出。SynchronousQueue
:不存储任务的队列,每个插入操作都必须等到另一线程取走任务,适合高并发短任务。PriorityBlockingQueue
:具有优先级的无界队列,根据任务优先级执行。
- 用于存储等待执行的任务。常用的队列类型包括:
-
threadFactory(线程工厂):
- 用于创建新线程,通常用于自定义线程名称或配置守护线程等。可以实现
ThreadFactory
接口,创建更有可读性或便于排查的线程名称。
- 用于创建新线程,通常用于自定义线程名称或配置守护线程等。可以实现
-
handler(拒绝策略):
- 当任务队列满且线程数量达到最大时,线程池需要执行的拒绝策略。常见的拒绝策略包括:
AbortPolicy
(默认):抛出RejectedExecutionException
异常。CallerRunsPolicy
:由调用线程执行该任务。DiscardPolicy
:直接丢弃任务,不抛异常。DiscardOldestPolicy
:丢弃队列中最早的任务,然后重试提交当前任务。
- 当任务队列满且线程数量达到最大时,线程池需要执行的拒绝策略。常见的拒绝策略包括:
3. 使用示例
public class ThreadPoolExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // unit
new LinkedBlockingQueue<>(100) // workQueue
);
// 提交任务
for (int i = 0; i < 20; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " is executing task.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
4. 注意事项
- 合理配置参数:确保
corePoolSize
、maximumPoolSize
和workQueue
大小合理,以免线程池资源耗尽或任务丢失。 - 避免阻塞任务:如果线程池中运行的任务需要长时间阻塞,可能会导致其他任务延迟执行,影响性能。
- 监控线程池:通过
ThreadPoolExecutor
提供的getActiveCount()
、getQueue().size()
等方法可以监控线程池的运行状态,适时调整参数。
线程池的合理使用能够有效控制系统的资源分配与管理,提升程序的并发能力及性能。