Java多线程实现之线程池详解
Java多线程实现之线程池详解
- 一、线程池的基本概念
- 1.1 为什么需要线程池
- 1.2 线程池的核心思想
- 二、Java线程池的实现
- 2.1 Executor框架
- 2.2 ThreadPoolExecutor构造参数
- 三、常见线程池类型
- 3.1 FixedThreadPool
- 3.2 CachedThreadPool
- 3.3 SingleThreadExecutor
- 3.4 ScheduledThreadPool
- 四、线程池的工作流程
- 五、线程池的使用示例
- 5.1 提交Runnable任务
- 5.2 提交Callable任务
- 5.3 定时任务
- 六、线程池的拒绝策略
- 6.1 AbortPolicy(默认)
- 6.2 CallerRunsPolicy
- 6.3 DiscardPolicy
- 6.4 DiscardOldestPolicy
- 七、线程池的监控与调优
- 7.1 监控线程池状态
- 7.2 合理配置线程池大小
- 八、线程池的最佳实践
- 8.1 避免使用Executors工厂方法
- 8.2 正确关闭线程池
- 8.3 使用自定义线程工厂
线程池在Java中是一种重要工具,用于管理和复用线程资源,合理使用线程池可以提高程序性能、减少资源消耗,并简化线程管理。本文我将详细介绍Java线程池的实现原理、常见类型以及使用方法等等,帮你更好地理解和深入Java的多线程实现。
一、线程池的基本概念
1.1 为什么需要线程池
- 线程创建和销毁开销大:频繁创建和销毁线程会消耗大量系统资源
- 资源管理困难:无限制创建线程可能导致系统资源耗尽
- 控制并发度:线程池可以限制同时运行的线程数量,避免过度并发
1.2 线程池的核心思想
线程池预先创建一定数量的线程,当有任务提交时,从线程池中获取线程执行任务,任务执行完毕后线程不会销毁,而是返回线程池等待下一个任务。
二、Java线程池的实现
2.1 Executor框架
Java通过Executor
框架提供线程池的实现,核心接口和类如下:
- Executor:线程池的基础接口,定义了执行任务的方法
- ExecutorService:扩展了
Executor
,提供了管理线程池的方法 - ThreadPoolExecutor:线程池的核心实现类
- ScheduledExecutorService:支持定时任务的线程池接口
- Executors:工具类,提供创建线程池的静态工厂方法
2.2 ThreadPoolExecutor构造参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数long keepAliveTime, // 空闲线程存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // 任务队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
)
三、常见线程池类型
3.1 FixedThreadPool
固定大小的线程池,核心线程数和最大线程数相等:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
3.2 CachedThreadPool
可缓存的线程池,线程数根据需要动态调整:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
3.3 SingleThreadExecutor
单线程执行器,确保所有任务按顺序执行:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
3.4 ScheduledThreadPool
支持定时任务的线程池:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
四、线程池的工作流程
- 提交任务:调用
execute()
或submit()
方法提交任务 - 线程池判断:
- 如果当前线程数小于核心线程数,创建新线程执行任务
- 如果当前线程数大于等于核心线程数,将任务放入任务队列
- 如果任务队列已满且线程数小于最大线程数,创建新线程执行任务
- 如果任务队列已满且线程数大于等于最大线程数,执行拒绝策略
- 任务执行完毕:线程返回线程池,等待下一个任务
五、线程池的使用示例
5.1 提交Runnable任务
ExecutorService executor = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {final int taskId = i;executor.execute(() -> {System.out.println("执行任务: " + taskId);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("任务" + taskId + "执行完毕");});
}executor.shutdown(); // 关闭线程池
5.2 提交Callable任务
ExecutorService executor = Executors.newFixedThreadPool(3);Future<String> future = executor.submit(() -> {Thread.sleep(2000);return "任务执行结果";
});try {String result = future.get(); // 获取任务结果System.out.println("结果: " + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}executor.shutdown();
5.3 定时任务
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);// 延迟3秒后执行任务
scheduler.schedule(() -> {System.out.println("延迟任务执行");
}, 3, TimeUnit.SECONDS);// 延迟1秒后开始执行,每2秒执行一次
scheduler.scheduleAtFixedRate(() -> {System.out.println("定时任务执行");
}, 1, 2, TimeUnit.SECONDS);
六、线程池的拒绝策略
6.1 AbortPolicy(默认)
直接抛出RejectedExecutionException
异常:
new ThreadPoolExecutor.AbortPolicy()
6.2 CallerRunsPolicy
由提交任务的线程自己执行该任务:
new ThreadPoolExecutor.CallerRunsPolicy()
6.3 DiscardPolicy
直接丢弃任务,不做任何处理:
new ThreadPoolExecutor.DiscardPolicy()
6.4 DiscardOldestPolicy
丢弃任务队列中最老的任务,然后尝试提交新任务:
new ThreadPoolExecutor.DiscardOldestPolicy()
七、线程池的监控与调优
7.1 监控线程池状态
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
);// 获取线程池状态
int activeCount = executor.getActiveCount(); // 活跃线程数
long completedTaskCount = executor.getCompletedTaskCount(); // 已完成任务数
int queueSize = executor.getQueue().size(); // 队列中的任务数
7.2 合理配置线程池大小
- CPU密集型任务:线程数 = CPU核心数 + 1
- IO密集型任务:线程数 = CPU核心数 * (1 + 平均等待时间/平均处理时间)
八、线程池的最佳实践
8.1 避免使用Executors工厂方法
Executors提供的工厂方法可能会创建出有资源耗尽风险的线程池,建议直接使用ThreadPoolExecutor
构造函数:
ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy()
);
8.2 正确关闭线程池
executor.shutdown(); // 平缓关闭,等待已提交任务执行完毕
// executor.shutdownNow(); // 立即关闭,尝试终止正在执行的任务try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}
} catch (InterruptedException e) {executor.shutdownNow();
}
8.3 使用自定义线程工厂
通过自定义线程工厂可以为线程设置有意义的名称,便于调试:
ThreadFactory namedThreadFactory = new ThreadFactory() {private final AtomicInteger threadNum = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "my-thread-" + threadNum.getAndIncrement());}
};
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ