Java 中的线程池是一种管理线程的机制,它通过重用预先创建的线程来提高性能。在 Java 中,java.util.concurrent.ExecutorService
接口提供了线程池的功能实现,而 java.util.concurrent.ThreadPoolExecutor
类则是实现了线程池功能的核心类。
线程池的执行流程
- 提交任务:当一个任务被提交给线程池时(通过
submit()
或execute()
方法),如果线程池中有空闲线程,则该任务会被立即执行。 - 队列:如果没有空闲线程,那么任务会被放入一个阻塞队列中等待执行。
- 创建新线程:如果队列满了并且线程数量少于最大线程数,线程池会创建新的工作线程来执行任务。
- 拒绝策略:如果队列满了且达到了最大线程数,线程池会采取拒绝策略来处理无法执行的任务。
拒绝策略
线程池的拒绝策略是指当线程池无法接受更多的任务时所采取的措施。ThreadPoolExecutor
提供了四种标准的拒绝策略:
- AbortPolicy:默认的拒绝策略,抛出
RejectedExecutionException
异常,表明任务已经被拒绝。 - CallerRunsPolicy:调用者的线程(提交任务的线程)将会执行该任务。如果线程池中的所有线程都忙于处理其他任务,则该策略会直接在调用
execute()
方法的线程上运行任务。 - DiscardPolicy:默默地丢弃无法处理的任务,不会抛出异常也不会通知调用者。
- DiscardOldestPolicy:如果不能执行当前任务,则尝试从阻塞队列中移除最旧的任务,并尝试再次添加当前任务。如果队列为空,则此策略等同于
DiscardPolicy
。
此外,还可以通过继承 RejectedExecutionHandler
接口来自定义拒绝策略。
示例代码
下面是一个简单的示例,展示如何创建一个线程池并设置不同的拒绝策略:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(2), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 5; i++) {
final int index = i;
try {
executor.execute(() -> {
System.out.println("Task " + index + " is running by " + Thread.currentThread().getName());
Thread.sleep(1000);
});
} catch (RejectedExecutionException e) {
System.out.println("Task " + index + " was rejected.");
}
}
// 关闭线程池
executor.shutdown();
}
}
在这个示例中,线程池的最大线程数为 4,队列大小为 2,因此当提交第 5 个任务时,由于没有可用的工作线程且队列已满,线程池会根据设置的拒绝策略来处理。这里我们设置了 CallerRunsPolicy
,所以第 5 个任务会在主线程中执行。
通过这种方式,可以根据实际需求选择合适的拒绝策略来处理线程池的溢出情况。