Java中ThreadPool线程池的详细使用指南
核心是在保证线程安全的情况下提速。线程池能够有效管理线程生命周期,避免频繁创建和销毁线程带来的性能开销,同时控制并发线程数量防止系统资源耗尽。
一 、任务主体不存在外部参数传入
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 定义任务数量为100个
int taskNum = 100;
// 创建CountDownLatch用于等待所有任务完成,参数为任务数量
final CountDownLatch latch = new CountDownLatch(taskNum);
// 创建固定大小的线程池,包含10个工作线程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
// 提交任务到线程池
for (int i = 0; i < taskNum; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 执行具体任务逻辑
executeTask();
} catch (Exception e) {
// 捕获异常并记录日志
insertLog(e.getMessage());
} finally {
// 无论任务成功与否,都减少计数器
latch.countDown();
}
}
});
}
// 关闭线程池,不再接受新任务
fixedThreadPool.shutdown();
try {
// 等待所有任务完成,最多阻塞当前线程
latch.await();
} catch (InterruptedException e) {
// 处理中断异常
insertLog(e.getMessage());
}
System.out.println("所有任务执行完成");
// 这里可以添加任务完成后的后续处理代码
}
/**
* 执行主体任务方法,不存在外部数据传入
* 示例:可以执行一些独立的任务,如生成报表、处理独立数据等
*/
public static void executeTask() {
System.out.println("正在执行独立任务...");
// 实际业务逻辑代码
}
/**
* 记录日志方法
* @param message 需要记录的日志信息
*/
public static void insertLog(String message) {
System.out.println("记录日志:" + message);
// 实际日志记录逻辑,如写入文件或数据库
}
}
二、存在传入参数的情况
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ParameterizedThreadPoolExample {
public static void main(String[] args) {
// 模拟创建需要执行的任务数据
List<String> dataList = new ArrayList<>();
for (int j = 1; j <= 5; j++) {
dataList.add("任务数据" + j);
}
// 任务数量和线程池大小
int taskNum = 5, poolNum = 5;
List<Runnable> taskList = new ArrayList<>();
// 创建任务列表
for (int i = 0; i < taskNum; i++) {
// 要传入线程的参数必须是final或effectively final
final String data = dataList.get(i);
taskList.add(new Runnable() {
@Override
public void run() {
// 执行带参数的任务
executeTask(data);
}
});
}
// 执行任务列表
executeTasks(taskList, taskNum, poolNum);
}
/**
* 封装的线程池执行方法
* @param taskList 任务列表
* @param taskNum 任务数量
* @param poolNum 线程池大小
*/
public static void executeTasks(List<Runnable> taskList, Integer taskNum, Integer poolNum) {
// 创建计数器
final CountDownLatch latch = new CountDownLatch(taskNum);
// 创建固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(poolNum);
// 提交任务到线程池
for (final Runnable task : taskList) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 执行任务
task.run();
} catch (Exception e) {
// 异常处理
insertLog(e.getMessage());
} finally {
// 计数器减一
latch.countDown();
}
}
});
}
// 关闭线程池
fixedThreadPool.shutdown();
try {
// 等待所有任务完成
latch.await();
} catch (InterruptedException e) {
insertLog(e.getMessage());
}
System.out.println("所有任务执行完成");
}
/**
* 执行带参数的任务
* @param data 传入的任务数据
*/
public static void executeTask(String data) {
System.out.println("正在处理任务数据:" + data);
// 实际业务处理逻辑
}
/**
* 记录日志方法
* @param message 日志信息
*/
public static void insertLog(String message) {
System.out.println("错误日志:" + message);
}
}
三、带超时控制的线程池使用
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TimeoutThreadPoolExample {
public static void main(String[] args) {
// 模拟创建任务数据
List<String> dataList = new ArrayList<>();
for (int j = 1; j <= 5; j++) {
dataList.add("任务数据" + j);
}
// 任务数量和线程池大小
int taskNum = 5, poolNum = 5;
List<Runnable> taskList = new ArrayList<>();
// 创建任务列表
for (int i = 0; i < taskNum; i++) {
final String data = dataList.get(i);
taskList.add(new Runnable() {
@Override
public void run() {
executeTask(data);
}
});
}
// 执行带超时控制的线程池任务
executeTasksWithTimeout(taskList, taskNum, poolNum);
}
/**
* 带超时控制的线程池执行方法
* @param taskList 任务列表
* @param taskNum 任务数量
* @param poolNum 线程池大小
*/
public static void executeTasksWithTimeout(List<Runnable> taskList, Integer taskNum, Integer poolNum) {
// 创建固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(poolNum);
// 提交所有任务
for (final Runnable task : taskList) {
fixedThreadPool.execute(task);
}
// 关闭线程池
fixedThreadPool.shutdown();
try {
// 设置超时等待,1表示1个单位,TimeUnit.HOURS表示单位是小时
// 最多等待1小时,要么提前执行完结束,要么超时返回
boolean isSuccess = fixedThreadPool.awaitTermination(1, TimeUnit.HOURS);
// 处理超时情况
if(!isSuccess){
System.out.println("线程执行超时,执行超时处理逻辑");
// 可以添加强制终止线程池的逻辑
// fixedThreadPool.shutdownNow();
}
} catch (InterruptedException e) {
insertLog(e.getMessage());
}
System.out.println("所有任务执行完成");
}
/**
* 执行带参数的任务
* @param data 任务数据
*/
public static void executeTask(String data) {
System.out.println("处理任务:" + data);
try {
// 模拟任务执行时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 记录日志方法
* @param message 日志信息
*/
public static void insertLog(String message) {
System.out.println("系统日志:" + message);
}
}
使用场景说明
- 无参数任务:适用于批量处理独立任务,如生成报表、发送通知等
- 带参数任务:适用于处理不同输入数据的任务,如处理不同用户请求、转换不同文件等
- 带超时控制:适用于对执行时间有要求的场景,如接口调用、定时任务等
注意事项
- 线程池大小应根据系统资源和任务特性合理设置
- 传入线程的参数必须是final或effectively final
- 超时时间应根据实际业务需求合理设置
- 注意异常处理和资源释放
- 对于长时间运行的任务,应考虑添加中断机制