1. ThreadPoolExecutor
构造方法
ThreadPoolExecutor
是 Java 并发库中的核心类,用于实现线程池。它的构造方法如下:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程的空闲保持时间
TimeUnit unit, // 空闲时间的时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
参数解释:
- corePoolSize:核心线程数,即使线程是空闲的,也不会被销毁的线程数。
- maximumPoolSize:最大线程数,当任务较多时,线程池允许创建的最大线程数量。
- keepAliveTime:非核心线程的空闲时间,超过该时间的空闲线程将被销毁。
- unit:
keepAliveTime
参数的时间单位。 - workQueue:用于存放等待执行任务的队列,通常使用
BlockingQueue
的实现类,如LinkedBlockingQueue
、SynchronousQueue
、ArrayBlockingQueue
等。 - threadFactory:线程工厂,用于创建新线程,通常可以用来给线程设置名字、优先级等。
- handler:拒绝策略,当任务过多且队列已满时,线程池如何处理新任务。可以选择 Java 提供的四种拒绝策略,也可以自定义。
2. 自定义线程池的步骤
自定义线程池的步骤包括以下几个方面:
- 选择合适的任务队列(
BlockingQueue
):根据应用场景选择适当的任务队列类型。 - 自定义线程工厂(
ThreadFactory
):通过自定义线程工厂,可以为线程设置特定的名称、优先级等。 - 选择或自定义拒绝策略(
RejectedExecutionHandler
):根据任务负载情况选择合适的拒绝策略,或者自定义拒绝策略。 - 配置核心线程数和最大线程数:根据实际需求合理配置核心线程数和最大线程数。
- 配置空闲线程存活时间:根据任务的执行时间和频率,设置适当的空闲线程存活时间。
3. 自定义线程池的示例
下面是一个自定义线程池的示例,其中包含了自定义线程工厂、自定义拒绝策略,并使用了 LinkedBlockingQueue
作为任务队列。
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 自定义线程工厂类
class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + "-thread-" + threadNumber.getAndIncrement());
System.out.println("创建新线程:" + t.getName());
return t;
}
}
// 自定义拒绝策略类
class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任务被拒绝:" + r.toString());
}
}
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 创建一个自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, // 非核心线程的空闲时间
TimeUnit.SECONDS, // 空闲时间的单位
new LinkedBlockingQueue<>(2), // 任务队列,容量为2
new CustomThreadFactory("CustomPool"), // 自定义线程工厂
new CustomRejectedExecutionHandler() // 自定义拒绝策略
);
// 提交多个任务给线程池
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(2000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
示例分析
-
自定义线程工厂
CustomThreadFactory
:- 通过实现
ThreadFactory
接口来自定义线程工厂,可以在创建新线程时为线程设置名称(方便调试和监控),也可以设置线程的优先级等属性。 - 本例中,线程名称被设置为
"CustomPool-thread-X"
的形式。
- 通过实现
-
自定义拒绝策略
CustomRejectedExecutionHandler
:- 通过实现
RejectedExecutionHandler
接口来自定义拒绝策略,当任务被拒绝时执行自定义操作(如记录日志、通知开发人员等)。 - 本例中,当任务被拒绝时,输出“任务被拒绝”的信息。
- 通过实现
-
自定义线程池
ThreadPoolExecutor
:- 设置核心线程数为 2,最大线程数为 4,使用
LinkedBlockingQueue
作为任务队列,队列容量为 2。 - 线程池可以同时运行 2 个核心线程,如果任务数量超过核心线程数,会先将任务放入队列。当队列已满时,如果线程数量小于最大线程数,则会创建新的线程来执行任务。如果线程数量已达到最大线程数且任务队列也已满,则执行自定义的拒绝策略。
- 设置核心线程数为 2,最大线程数为 4,使用
-
提交任务并关闭线程池:
- 提交了 10 个任务给线程池,其中前 4 个任务可以正常被执行(2 个核心线程 + 2 个排队任务)。从第 5 个任务开始,由于线程池已达到最大容量,后续任务将会被拒绝,并触发自定义的拒绝策略。
4. ThreadPoolExecutor
的拒绝策略
ThreadPoolExecutor
提供了四种内置的拒绝策略,当线程池无法接受新任务时,这些策略决定了如何处理新任务:
- AbortPolicy(默认):直接抛出
RejectedExecutionException
异常。 - CallerRunsPolicy:由调用线程(提交任务的线程)直接执行被拒绝的任务。
- DiscardPolicy:直接丢弃任务,不抛出任何异常。
- DiscardOldestPolicy:丢弃任务队列中最旧的任务,然后尝试提交新任务。
开发者可以根据实际需求选择适合的拒绝策略,或者通过实现 RejectedExecutionHandler
接口来自定义拒绝策略。
5. 自定义线程池的使用建议
-
根据任务特性选择适当的任务队列:例如,使用
LinkedBlockingQueue
可以缓解任务堆积压力,而SynchronousQueue
可以快速创建新线程处理任务,但可能会导致线程数量过多。 -
合理配置线程数和队列大小:根据应用场景和服务器硬件资源,合理配置核心线程数、最大线程数和队列大小,避免过多的上下文切换或资源耗尽。
-
优先选择内置的拒绝策略:如果内置的拒绝策略不能满足需求,可以自定义拒绝策略来更好地控制任务的拒绝行为。
-
监控和调优:在生产环境中,建议监控线程池的使用情况(如线程数量、队列大小、拒绝任务数等),根据实际运行情况不断调整线程池的参数配置。
6. 总结
自定义线程池是 Java 并发编程中非常重要的一项技能。通过自定义 ThreadPoolExecutor
,可以更灵活地控制线程池的行为和性能。
在 Java 中,自定义线程池可以通过使用 ThreadPoolExecutor
类来实现。ThreadPoolExecutor
是 Java 提供的一个灵活且功能强大的线程池实现类,允自定义线程池的各种参数(如核心线程数、最大线程数、任务队列、线程工厂、拒绝策略等)来满足特定的需求。