文章目录
- 1. 引言
- 2. 理论基础
- 3. Java线程池的实现类
- 4. 线程池的创建
- 5. 线程池的使用
- 6. 线程池的关闭
- 7. 线程池的参数配置
- 8. 实践与案例
- 案例 1 的实现
- 案例 2 的实现
- 案例 3 的实现
- 9. 常见问题与优化
- 问题 1:如何处理任务超时?
- 问题 2:线程池的监控与调优
- 问题 3:避免资源耗尽
- 总结
- 10. 总结与扩展
- 内容总结
- 知识扩展
- 自学资源与方法
- 11. 实验与练习
- 实验 1 的实现
- 步骤
- 代码实现
- 解释
- 运行和测试
- 实验 2 的实现
- 实验设计
- 代码实现
- 解释
- 运行和观察
1. 引言
- 目标: 了解Java线程池的概念、使用场景、创建方法及其优缺点。
- 预备知识: 基本的Java编程知识,线程和多线程的概念。
2. 理论基础
- 什么是线程池:
- 线程池是一种管理和重用线程的机制,避免了频繁创建和销毁线程的开销。
- 为什么需要线程池:
- 提高性能,减少资源消耗,控制线程数量,提高线程的可管理性。
3. Java线程池的实现类
- Executors框架:
- ExecutorService接口及其实现类:ThreadPoolExecutor。
- Executors工具类提供的快捷方法:newFixedThreadPool(), newSingleThreadExecutor(), newCachedThreadPool(), newScheduledThreadPool()。
4. 线程池的创建
- 使用Executors创建:
ExecutorService executor = Executors.newFixedThreadPool(5);
- 自定义ThreadPoolExecutor:
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
5. 线程池的使用
- 提交任务:
executor.submit(new Runnable() {@Overridepublic void run() {// 任务代码}
});
- 获取执行结果:
Future<String> future = executor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {return "Task Result";}
});
String result = future.get();
6. 线程池的关闭
- shutdown():不会立即停止线程池,而是等待所有任务完成后再关闭。
- shutdownNow():尝试停止所有正在执行的任务,并返回等待执行任务的列表。
7. 线程池的参数配置
- CorePoolSize:核心线程数。
- MaximumPoolSize:最大线程数。
- KeepAliveTime:线程空闲后的存活时间。
- WorkQueue:任务队列,常用类型有LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue。
- ThreadFactory:用于创建新线程的工厂。
- RejectedExecutionHandler:当线程池无法处理新任务时,使用的策略。
8. 实践与案例
- 案例1: 使用固定线程池处理大量短任务。
- 案例2: 使用缓存线程池处理不确定数量的任务。
- 案例3: 定时任务的实现。
案例 1 的实现
下面是一个使用固定线程池来处理大量短任务的 Java 示例。这个案例假设我们有大量的计算任务,每个任务执行时间较短,但总量较大,适合使用固定线程池来管理。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class FixedThreadPoolExample {public static void main(String[] args) {// 创建一个固定大小的线程池,这里我们设定为5个线程ExecutorService executor = Executors.newFixedThreadPool(5);// 假设我们有10个任务需要执行for (int i = 0; i < 10; i++) {final int taskId = i;// 提交任务到线程池executor.submit(() -> {// 模拟短任务performShortTask(taskId);});}// 关闭线程池,等待所有任务完成executor.shutdown();while (!executor.isTerminated()) {// 等待所有任务完成}System.out.println("Finished all threads");}// 模拟一个短任务private static void performShortTask(int taskId) {try {// 模拟任务执行时间Thread.sleep(1000);System.out.println("Task " + taskId + " completed by " + Thread.currentThread().getName());} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
解释:
- 创建线程池:
- 使用Executors.newFixedThreadPool(5)创建了一个固定大小的线程池,线程数为5。
- 提交任务:
- 使用for循环提交10个任务到线程池中。每个任务是一个lambda表达式,调用performShortTask方法。
- 模拟短任务:
- performShortTask方法通过Thread.sleep(1000)模拟一个执行时间为1秒的短任务。
- 关闭线程池:
- 使用executor.shutdown()关闭线程池,但不会立即终止线程池,而是等待所有任务完成。
- 通过while (!executor.isTerminated())循环等待所有任务完成。
- 输出:
- 每个任务完成后,输出任务ID和执行该任务的线程名称。
这个例子展示了如何使用固定线程池来处理大量的短任务,线程池会复用线程,从而避免了频繁创建和销毁线程的开销。
案例 2 的实现
下面是一个使用缓存线程池来处理不确定数量任务的Java示例。缓存线程池(CachedThreadPool)会根据需要创建新的线程,但如果有空闲线程,它会重用这些线程,从而减少了线程创建和销毁的开销。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CachedThreadPoolExample {public static void main(String[] args) {// 创建一个缓存线程池ExecutorService executor = Executors.newCachedThreadPool();// 模拟不确定数量的任务,这里我们假设有1到100之间的随机数量的任务int taskCount = (int) (Math.random() * 100) + 1;System.out.println("Total tasks to execute: " + taskCount);for (int i = 0; i < taskCount; i++) {final int taskId = i;// 提交任务到线程池executor.submit(() -> {// 模拟任务执行时间try {Thread.sleep((long) (Math.random() * 1000)); // 任务执行时间在0到1秒之间} catch (InterruptedException e) {Thread.currentThread().interrupt()