Java并发-线程池
大约 4 分钟
Java并发-线程池
核心理论
1.1 线程池的工作原理
线程池通过预先创建一定数量的线程,管理线程的生命周期,实现线程的复用。当有任务提交时,线程池会从池中分配空闲线程执行任务,任务完成后线程不会销毁,而是返回池中等待新任务。
1.2 线程池的核心参数
- 核心线程数(corePoolSize):线程池长期维持的线程数量
- 最大线程数(maximumPoolSize):线程池允许创建的最大线程数
- 队列容量(workQueue):用于缓存等待执行的任务的阻塞队列
- 拒绝策略(RejectedExecutionHandler):任务过多时的处理策略
- 空闲线程存活时间(keepAliveTime):非核心线程的空闲超时时间
- 线程工厂(ThreadFactory):用于创建线程的工厂类
1.3 常见线程池类型
- FixedThreadPool:固定核心线程数的线程池
- CachedThreadPool:可缓存的线程池,根据需求创建线程
- SingleThreadExecutor:单线程的线程池
- ScheduledThreadPool:支持定时任务的线程池
代码实践
2.1 自定义线程池实现
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), // 队列容量
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 10; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("任务" + taskId + "由线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
2.2 线程池监控与调优
public class ThreadPoolMonitorExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5)
);
// 定时打印线程池状态
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.println("线程池状态:");
System.out.println("核心线程数:" + executor.getCorePoolSize());
System.out.println("活动线程数:" + executor.getActiveCount());
System.out.println("已完成任务数:" + executor.getCompletedTaskCount());
System.out.println("队列任务数:" + executor.getQueue().size());
System.out.println("----------------------------------------");
}, 0, 1, TimeUnit.SECONDS);
// 提交任务
for (int i = 0; i < 10; i++) {
int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.shutdown();
monitor.shutdown();
}
}
设计思想
3.1 线程池的设计模式
- 享元模式:通过复用线程对象减少资源消耗
- 生产者-消费者模式:任务提交者作为生产者,线程池中的线程作为消费者
- 策略模式:拒绝策略的灵活切换
3.2 线程池参数调优策略
- CPU密集型任务:核心线程数 = CPU核心数 + 1
- IO密集型任务:核心线程数 = CPU核心数 * 2
- 混合型任务:拆分后分别处理或按IO密集型配置
- 队列选择:无界队列可能导致内存溢出,建议使用有界队列
避坑指南
4.1 避免使用Executors创建线程池
Executors创建的线程池可能存在以下问题:
- FixedThreadPool和SingleThreadExecutor使用无界队列,可能导致OOM
- CachedThreadPool允许创建无限线程,可能导致OOM 建议使用ThreadPoolExecutor手动创建线程池,明确核心参数
4.2 正确处理线程池中的异常
线程池提交任务有两种方式:execute()和submit()。execute()直接抛出异常,submit()将异常封装在Future中,需要通过get()方法获取。
4.3 线程池的优雅关闭
- shutdown():等待所有任务执行完毕后关闭
- shutdownNow():立即关闭,尝试中断正在执行的任务
- 建议使用shutdown() + awaitTermination()组合实现优雅关闭
深度思考题
- 线程池的工作队列有哪些类型?各有什么特点?
- 线程池的拒绝策略有哪些?如何自定义拒绝策略?
- 如何实现线程池的动态参数调整?
思考题回答:
常见的工作队列类型:
- ArrayBlockingQueue:基于数组的有界阻塞队列
- LinkedBlockingQueue:基于链表的可选有界阻塞队列
- SynchronousQueue:不存储元素的阻塞队列,直接提交给线程
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- DelayedWorkQueue:支持延迟执行的无界阻塞队列
拒绝策略包括:
- AbortPolicy:直接抛出RejectedExecutionException
- CallerRunsPolicy:由提交任务的线程执行任务
- DiscardPolicy:直接丢弃任务
- DiscardOldestPolicy:丢弃队列中最旧的任务 自定义拒绝策略需实现RejectedExecutionHandler接口
可以通过ThreadPoolExecutor的setCorePoolSize()、setMaximumPoolSize()等方法动态调整参数。结合监控系统,根据系统负载实时调整线程池参数。