Skip to content

线程池的常见代码题实现

一、是什么?

线程池是一种线程管理机制,通过预先创建一组可复用的线程,避免频繁创建/销毁线程的开销。Java中主要通过java.util.concurrent.ExecutorService接口及其实现类(如ThreadPoolExecutor)实现。

二、解决什么问题

  1. 减少资源消耗:线程创建/销毁开销大(约1ms/次)
  2. 控制并发数:防止无限制创建线程导致OOM
  3. 统一管理任务:提供任务队列、拒绝策略等机制
  4. 提高响应速度:任务到达时可直接使用空闲线程

三、核心方法(JDK8+)

方法说明
newFixedThreadPool()固定大小线程池
newCachedThreadPool()弹性线程池(无大小限制)
newScheduledThreadPool()支持定时任务的线程池
submit()提交任务(返回Future)
shutdown()优雅关闭(处理完队列任务)
shutdownNow()立即关闭(返回未处理任务)

四、应用场景

  1. Web服务器请求处理
  2. 批量数据处理(如Excel导入)
  3. 异步任务执行(如发送短信/邮件)
  4. 定时任务调度(替代Timer)

五、代码示例(含JDK8新特性)

java
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建线程池(推荐手动创建)
        ExecutorService pool = new ThreadPoolExecutor(
            2, // 核心线程数
            5, // 最大线程数
            60, // 空闲线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(10), // 任务队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );

        // 2. 提交任务(使用Lambda表达式)
        for (int i = 0; i < 15; i++) {
            final int taskId = i;
            pool.submit(() -> {
                System.out.println(Thread.currentThread().getName() 
                    + " 执行任务-" + taskId);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 3. 关闭线程池(JDK8+优雅关闭)
        pool.shutdown();
        try {
            if (!pool.awaitTermination(1, TimeUnit.MINUTES)) {
                pool.shutdownNow();
            }
        } catch (InterruptedException e) {
            pool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

六、执行流程可视化

七、重要注意事项

  1. 禁止使用Executors创建线程池(易导致OOM)
    • newFixedThreadPool:使用无界队列(LinkedBlockingQueue)
    • newCachedThreadPool:最大线程数=Integer.MAX_VALUE
  2. 合理配置参数
    • CPU密集型:线程数 = CPU核心数 + 1
    • IO密集型:线程数 = CPU核心数 * 2
  3. 必须处理拒绝策略(默认AbortPolicy会抛异常)
    • CallerRunsPolicy:由提交任务的线程执行
    • DiscardOldestPolicy:丢弃最旧任务
  4. 使用ThreadLocal需谨慎:线程复用可能导致数据污染
  5. 优雅关闭shutdown() + awaitTermination()组合

八、JDK8+增强特性

  1. CompletableFuture:更强大的异步编程
    java
    CompletableFuture.supplyAsync(() -> "result", pool)
        .thenApplyAsync(s -> s.toUpperCase())
        .thenAccept(System.out::println);
  2. 并行流(Parallel Stream):底层使用ForkJoinPool
    java
    IntStream.range(1,100).parallel().forEach(i -> process(i));

九、常见面试题实现

  1. 交替打印ABC
java
ExecutorService pool = Executors.newFixedThreadPool(3);
AtomicInteger count = new AtomicInteger(0);

for (char c : "ABC".toCharArray()) {
    pool.execute(() -> {
        while (count.get() < 30) {
            synchronized (count) {
                if (count.get() % 3 == c - 'A') {
                    System.out.print(c);
                    count.incrementAndGet();
                    count.notifyAll();
                } else {
                    try { count.wait(); } 
                    catch (InterruptedException e) {}
                }
            }
        }
    });
}
pool.shutdown();
  1. 批量任务结果收集
java
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    final int id = i;
    tasks.add(() -> "Result-" + id);
}

List<Future<String>> futures = pool.invokeAll(tasks);
futures.stream().map(f -> {
    try { return f.get(); } 
    catch (Exception e) { return null; }
}).forEach(System.out::println);

十、总结

要点说明
创建原则使用ThreadPoolExecutor构造函数
参数配置根据任务类型设置core/max大小
队列选择CPU密集型选SynchronousQueue,IO密集型选LinkedBlockingQueue
关闭规范必须调用shutdown(),配合awaitTermination
新特性优先使用CompletableFuture替代Future

最佳实践:生产环境建议使用Guava的ThreadFactoryBuilder或Spring的ThreadPoolTaskExecutor,提供更完善的监控和管理功能。