Java线程池使用示例
下面我将展示如何使用自定义线程池(ThreadPoolExecutor
)来执行任务,包括提交Runnable任务、Callable任务,处理拒绝策略以及关闭线程池。
import java.util.concurrent.*;
public class ThreadPoolDemo {
// 自定义线程工厂(为线程命名)
static class CustomThreadFactory implements ThreadFactory {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
count++;
Thread thread = new Thread(r);
thread.setName("Custom-Thread-" + count);
thread.setDaemon(false); // 设置为非守护线程
return thread;
}
}
public static void main(String[] args) throws Exception {
// 创建线程池
ExecutorService customPool = new ThreadPoolExecutor(
3, // 核心线程数
6, // 最大线程数
5, TimeUnit.SECONDS, // 非核心线程空闲5秒销毁
new ArrayBlockingQueue<>(5), // 有界队列(容量5)
new CustomThreadFactory(), // 自定义线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
System.out.println("提交15个任务到线程池:");
System.out.println("队列容量=5,最大线程=6 -> 最多同时处理11个任务");
// 提交15个任务(超出最大处理能力)
for (int i = 1; i <= 15; i++) {
final int taskId = i;
customPool.submit(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 执行任务-" + taskId);
// 模拟任务执行时间
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 提交有返回值的任务
Future<String> future = customPool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 执行Callable任务");
Thread.sleep(800);
return "任务执行成功!";
});
try {
System.out.println("\nCallable任务结果: " + future.get(2, TimeUnit.SECONDS));
} catch (TimeoutException e) {
System.err.println("任务超时未完成");
} catch (ExecutionException e) {
System.err.println("任务执行异常: " + e.getCause());
}
// 等待所有任务基本完成
Thread.sleep(1500);
// 关闭线程池(优雅关闭)
shutdownThreadPool(customPool);
}
// 优雅关闭线程池的方法
private static void shutdownThreadPool(ExecutorService pool) {
try {
System.out.println("\n正在关闭线程池...");
pool.shutdown(); // 拒绝新任务
if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("强制终止运行中的任务...");
pool.shutdownNow(); // 尝试终止正在执行的任务
}
System.out.println("线程池已关闭");
} catch (InterruptedException e) {
System.err.println("关闭线程池时被中断: " + e.getMessage());
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
使用说明
1. 线程池关键参数
- 核心线程数:3个
- 最大线程数:6个(当队列满时,会创建新线程直到6个)
- 任务队列:容量为5的有限队列(
ArrayBlockingQueue
) - 拒绝策略:
CallerRunsPolicy
(当队列满且线程数达最大值时,由调用线程自己执行任务)
2. 线程池工作流程
graph TD
A[提交任务] --> B{是否超过核心线程数?}
B --> |否| C[创建核心线程执行]
B --> |是| D{队列是否已满?}
D --> |否| E[任务入队列等待]
D --> |是| F{线程数是否达到最大?}
F --> |否| G[创建新线程执行]
F --> |是| H[触发拒绝策略]
3. 拒绝策略的行为示例
- 在代码中我们提交了15个任务,但最大处理能力只有11个
- 超出能力后:
- 第12-15个任务将由
CallerRunsPolicy
策略处理 - 调用
submit()
的主线程(main)会直接执行这些任务
- 第12-15个任务将由
- 执行时会看到main线程执行任务的输出
4. 最佳实践建议
-
任务提交:
- 使用
execute()
提交不需要返回值的任务 - 使用
submit()
提交需要返回值的任务 - 使用
invokeAll()
提交批量任务
- 使用
-
异常处理:
- 在任务内捕获所有异常
- 使用
Future.get()
捕获执行异常
-
优雅关闭:
shutdown()
: 停止接受新任务,继续执行队列中的任务awaitTermination(timeout)
: 等待执行完成shutdownNow()
: 尝试中断所有任务
-
监控线程池:
ThreadPoolExecutor executor = (ThreadPoolExecutor) customPool;
System.out.println("活跃线程数: " + executor.getActiveCount());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
System.out.println("队列大小: " + executor.getQueue().size());
这个示例完整展示了线程池的使用方式,包括自定义线程工厂、提交任务、获取结果、异常处理和优雅关闭的完整流程。