线程池深度解析

43 阅读9分钟

线程池深度解析:从使用到源码,再到生产实践

一、为什么需要线程池?

1.1 线程的代价

在开始讲解线程池之前,我们先看一个简单的例子:

// 原始方式:每次请求都创建新线程
public void handleRequest(Request request) {
    new Thread(() -> {
        // 处理业务逻辑
        process(request);
    }).start();
}

这种方式有什么问题?

  • 创建成本高:每次创建线程需要分配内存、初始化栈(默认1MB)
  • 资源浪费:线程创建/销毁消耗CPU资源
  • 难以管理:线程数量无限制,容易导致系统崩溃

1.2 线程池的救赎

// 使用线程池后的优雅方式
private ExecutorService threadPool = Executors.newFixedThreadPool(10);

public void handleRequest(Request request) {
    threadPool.execute(() -> {
        process(request);
    });
}

二、Java线程池家族

2.1 Executors工厂类

Java提供了4种常用的线程池:

// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 适用场景:已知并发量,需要控制资源

// 2. 单线程线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 适用场景:任务需要顺序执行

// 3. 可缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 适用场景:短期异步任务,线程数自动调整

// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
// 适用场景:延迟执行或周期性任务

2.2 这些"快捷方式"的真相

我们点开源码看看:

// Executors.newFixedThreadPool源码
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
        nThreads,                  // 核心线程数
        nThreads,                  // 最大线程数
        0L, TimeUnit.MILLISECONDS, // 空闲线程存活时间
        new LinkedBlockingQueue<Runnable>() // 无界队列
    );
}

注意坑点LinkedBlockingQueue默认是Integer.MAX_VALUE,任务堆积可能OOM!

三、深入ThreadPoolExecutor源码

3.1 核心参数解析

public ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数(长期存活的线程)
    int maximumPoolSize,   // 最大线程数
    long keepAliveTime,    // 空闲线程存活时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue,  // 工作队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

3.2 线程池工作流程(源码级解析)

// 简化版的execute方法流程
public void execute(Runnable command) {
    // 第一阶段:核心线程处理
    if (workerCount < corePoolSize) {
        if (addWorker(command, true))  // 创建核心线程
            return;
    }
    
    // 第二阶段:任务入队
    if (isRunning() && workQueue.offer(command)) {
        // 再次检查
        if (!isRunning() && remove(command))
            reject(command);
        else if (workerCount == 0)
            addWorker(null, false);
    }
    
    // 第三阶段:创建非核心线程
    else if (!addWorker(command, false))
        // 第四阶段:拒绝策略
        reject(command);
}

让我们用流程图更直观地理解:

任务提交
    ↓
当前线程数 < corePoolSize?
    ├── 是 → 创建核心线程执行任务
    ↓
    └── 否 → 工作队列未满?
        ├── 是 → 任务入队等待
        ↓
        └── 否 → 当前线程数 < maximumPoolSize?
            ├── 是 → 创建临时线程执行
            ↓
            └── 否 → 执行拒绝策略

3.3 Worker内部类:线程池的真正执行者

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;  // 实际执行任务的线程
    Runnable firstTask;   // 初始任务
    
    Worker(Runnable firstTask) {
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        runWorker(this);  // 核心执行方法
    }
    
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        
        while (task != null || (task = getTask()) != null) {
            // 获取锁,确保线程不会被其他任务中断
            w.lock();
            
            try {
                // 执行前的钩子方法
                beforeExecute(wt, task);
                
                try {
                    task.run();  // 执行任务!
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;  // 统计完成任务数
                w.unlock();
            }
        }
        
        processWorkerExit(w, completedAbruptly);
    }
}

3.4 getTask():线程如何获取任务

private Runnable getTask() {
    boolean timedOut = false; // 上次poll是否超时
    
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        
        // 检查线程池状态
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        
        int wc = workerCountOf(c);
        
        // 判断是否允许超时回收线程
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        
        try {
            // 关键:从队列取任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

四、拒绝策略详解

4.1 Java内置的4种拒绝策略

// 1. AbortPolicy(默认) - 直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task rejected");
}

// 2. CallerRunsPolicy - 让调用者线程执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();  // 注意:是run()不是start()!
    }
}

// 3. DiscardPolicy - 默默丢弃
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    // 什么都不做,直接丢弃
}

// 4. DiscardOldestPolicy - 丢弃队列最老任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();  // 丢弃队头
        e.execute(r);         // 尝试重新执行
    }
}

4.2 自定义拒绝策略实战

@Component
public class CustomRejectPolicy implements RejectedExecutionHandler {
    
    private final MeterRegistry meterRegistry;
    
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 1. 记录指标
        meterRegistry.counter("threadpool.rejected.tasks").increment();
        
        // 2. 降级策略
        if (r instanceof ImportantTask) {
            // 重要任务:记录日志,等待重试
            log.warn("重要任务被拒绝,放入重试队列");
            retryQueue.offer((ImportantTask) r);
        } else {
            // 普通任务:异步持久化,稍后处理
            asyncSaveToDB(r);
        }
        
        // 3. 告警通知
        if (needAlert(executor)) {
            sendAlert(executor);
        }
    }
}

五、生产环境线程池配置方案

5.1 线程数计算公式

// 根据任务类型设置线程数
public class ThreadPoolConfig {
    
    /**
     * CPU密集型任务(计算、加密等)
     * 线程数 = CPU核心数 + 1
     */
    public static ExecutorService cpuIntensivePool() {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cpuCores + 1,
            cpuCores + 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000)
        );
    }
    
    /**
     * IO密集型任务(网络请求、数据库操作等)
     * 线程数 = CPU核心数 * (1 + 等待时间/计算时间)
     * 通常设置为 CPU核心数 * 2 ~ 5
     */
    public static ExecutorService ioIntensivePool() {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cpuCores * 2,
            cpuCores * 5,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2000)
        );
    }
    
    /**
     * 混合型任务
     * 最佳线程数 = CPU核心数 * (1 + 阻塞系数)
     * 阻塞系数 = 阻塞时间 / (阻塞时间 + 计算时间)
     */
    public static ExecutorService mixedPool(double blockingCoefficient) {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        int optimalThreads = (int) (cpuCores * (1 + blockingCoefficient));
        
        return new ThreadPoolExecutor(
            optimalThreads,
            optimalThreads * 2,
            30L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(optimalThreads * 10)
        );
    }
}

5.2 Spring Boot中的最佳配置

# application.yml
thread-pool:
  configs:
    order-process:
      core-size: 8
      max-size: 16
      queue-capacity: 100
      keep-alive-seconds: 60
      thread-name-prefix: order-
      reject-policy: CALLER_RUNS
      
    payment-callback:
      core-size: 4
      max-size: 8
      queue-capacity: 200
      thread-name-prefix: payment-
      reject-policy: ABORT
      
    report-generate:
      core-size: 2
      max-size: 4
      queue-capacity: 1000
      thread-name-prefix: report-
      allow-core-thread-timeout: true
@Configuration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {
    
    @Bean
    public Map<String, ExecutorService> threadPools(
            ThreadPoolProperties properties) {
        
        Map<String, ExecutorService> pools = new HashMap<>();
        
        properties.getConfigs().forEach((name, config) -> {
            // 动态参数配置
            DynamicThreadPoolExecutor executor = 
                new DynamicThreadPoolExecutor(
                    config.getCoreSize(),
                    config.getMaxSize(),
                    config.getKeepAliveSeconds(),
                    TimeUnit.SECONDS,
                    createQueue(config.getQueueCapacity()),
                    new CustomThreadFactory(config.getThreadNamePrefix()),
                    createRejectPolicy(config.getRejectPolicy())
                );
            
            // 允许核心线程超时
            if (config.isAllowCoreThreadTimeout()) {
                executor.allowCoreThreadTimeOut(true);
            }
            
            // 注册监控
            ThreadPoolMonitor.register(name, executor);
            
            pools.put(name, executor);
        });
        
        return pools;
    }
    
    private BlockingQueue<Runnable> createQueue(int capacity) {
        // 根据队列大小选择队列类型
        if (capacity <= 0) {
            return new SynchronousQueue<>();
        } else if (capacity <= 10000) {
            return new ArrayBlockingQueue<>(capacity);
        } else {
            return new LinkedBlockingQueue<>(capacity);
        }
    }
}

5.3 动态线程池:美团动态线程池实践

@Component
public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {
    
    private final String poolName;
    private final ThreadPoolProperties properties;
    
    public DynamicThreadPoolExecutor(String poolName, 
                                     ThreadPoolProperties properties) {
        super(properties.getCoreSize(), 
              properties.getMaxSize(),
              properties.getKeepAliveSeconds(), 
              TimeUnit.SECONDS,
              new ResizableCapacityLinkedBlockingQueue<>(
                  properties.getQueueCapacity()
              ));
        this.poolName = poolName;
        this.properties = properties;
        
        // 初始化监听
        initListeners();
    }
    
    /**
     * 动态调整核心线程数
     */
    public void setCorePoolSize(int corePoolSize) {
        if (corePoolSize >= 0 && 
            corePoolSize <= getMaximumPoolSize()) {
            super.setCorePoolSize(corePoolSize);
            
            // 记录变更
            log.info("线程池{}核心线程数调整为: {}", 
                     poolName, corePoolSize);
        }
    }
    
    /**
     * 动态调整最大线程数
     */
    public void setMaximumPoolSize(int maximumPoolSize) {
        if (maximumPoolSize >= getCorePoolSize()) {
            super.setMaximumPoolSize(maximumPoolSize);
            
            // 记录变更
            log.info("线程池{}最大线程数调整为: {}", 
                     poolName, maximumPoolSize);
        }
    }
    
    /**
     * 动态调整队列容量
     */
    public void setQueueCapacity(int capacity) {
        ResizableCapacityLinkedBlockingQueue<Runnable> queue = 
            (ResizableCapacityLinkedBlockingQueue<Runnable>) getQueue();
        queue.setCapacity(capacity);
        
        log.info("线程池{}队列容量调整为: {}", poolName, capacity);
    }
}

// 可调整容量的队列
public class ResizableCapacityLinkedBlockingQueue<E> 
        extends LinkedBlockingQueue<E> {
    
    private volatile int capacity;
    
    public ResizableCapacityLinkedBlockingQueue(int capacity) {
        super(capacity);
        this.capacity = capacity;
    }
    
    public synchronized void setCapacity(int newCapacity) {
        if (newCapacity <= 0) {
            throw new IllegalArgumentException();
        }
        
        // 调整容量
        this.capacity = newCapacity;
        
        // 如果新容量小于当前size,需要移除多余元素
        while (size() > newCapacity) {
            poll();
        }
    }
    
    @Override
    public boolean offer(E e) {
        // 动态判断是否接受新任务
        if (size() >= capacity) {
            return false;
        }
        return super.offer(e);
    }
}

六、线程池监控方案

6.1 监控指标体系

@Component
public class ThreadPoolMonitor {
    
    private final MeterRegistry meterRegistry;
    
    public void monitor(String poolName, ThreadPoolExecutor executor) {
        // 1. 核心指标
        monitorCoreMetrics(poolName, executor);
        
        // 2. 队列指标
        monitorQueueMetrics(poolName, executor);
        
        // 3. 任务执行指标
        monitorTaskMetrics(poolName, executor);
        
        // 4. 线程状态指标
        monitorThreadMetrics(poolName, executor);
    }
    
    private void monitorCoreMetrics(String poolName, 
                                    ThreadPoolExecutor executor) {
        // 线程数指标
        Gauge.builder("threadpool.core.size", 
                     executor, ThreadPoolExecutor::getCorePoolSize)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.active.count",
                     executor, ThreadPoolExecutor::getActiveCount)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.largest.pool.size",
                     executor, ThreadPoolExecutor::getLargestPoolSize)
            .tag("pool", poolName)
            .register(meterRegistry);
    }
    
    private void monitorQueueMetrics(String poolName,
                                     ThreadPoolExecutor executor) {
        BlockingQueue<Runnable> queue = executor.getQueue();
        
        Gauge.builder("threadpool.queue.size",
                     queue, BlockingQueue::size)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.queue.remaining",
                     queue, q -> q.remainingCapacity())
            .tag("pool", poolName)
            .register(meterRegistry);
    }
}

6.2 可视化监控面板(Grafana)

{
  "panels": [
    {
      "title": "线程池状态",
      "targets": [
        {
          "expr": "threadpool_active_count{pool=\"$pool\"}",
          "legendFormat": "活跃线程"
        },
        {
          "expr": "threadpool_core_size{pool=\"$pool\"}",
          "legendFormat": "核心线程"
        },
        {
          "expr": "threadpool_max_size{pool=\"$pool\"}",
          "legendFormat": "最大线程"
        }
      ]
    },
    {
      "title": "队列监控",
      "targets": [
        {
          "expr": "threadpool_queue_size{pool=\"$pool\"}",
          "legendFormat": "队列大小"
        },
        {
          "expr": "threadpool_queue_remaining{pool=\"$pool\"}",
          "legendFormat": "队列剩余容量"
        }
      ]
    }
  ]
}

6.3 告警规则配置

# alert-rules.yml
groups:
  - name: threadpool_alerts
    rules:
      - alert: ThreadPoolQueueFull
        expr: threadpool_queue_remaining{pool="order-process"} == 0
        for: 1m
        annotations:
          summary: "订单线程池队列已满"
          description: "{{ $labels.pool }}队列已满,持续1分钟"
          
      - alert: ThreadPoolRejectedTasksHigh
        expr: rate(threadpool_rejected_tasks_total{pool="payment-callback"}[5m]) > 10
        for: 30s
        annotations:
          summary: "支付回调线程池拒绝任务过多"
          description: "{{ $labels.pool }}5分钟内拒绝任务超过10个"
          
      - alert: ThreadPoolActiveThreadsHigh
        expr: threadpool_active_count{pool="report-generate"} / threadpool_max_size{pool="report-generate"} > 0.8
        for: 2m
        annotations:
          summary: "报表生成线程池使用率过高"
          description: "{{ $labels.pool }}活跃线程数达到最大线程数的80%"

七、常见问题与解决方案

7.1 线程池中的线程异常怎么办?

public class SafeThreadPoolExecutor extends ThreadPoolExecutor {
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        
        // 处理未捕获异常
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        
        if (t != null) {
            // 记录异常并采取恢复措施
            log.error("线程池任务执行异常", t);
            handleException(t);
        }
    }
    
    private void handleException(Throwable t) {
        // 1. 发送告警
        alertSystem.send(t);
        
        // 2. 重试机制
        if (canRetry(t)) {
            retryQueue.offer(currentTask);
        }
        
        // 3. 降级处理
        fallbackService.process(currentTask);
    }
}

7.2 如何优雅关闭线程池?

@Component
public class ThreadPoolShutdownHandler {
    
    @PreDestroy
    public void shutdownAll() {
        // 1. 拒绝新任务
        threadPool.shutdown();
        
        try {
            // 2. 等待现有任务完成(最多等待30分钟)
            if (!threadPool.awaitTermination(30, TimeUnit.MINUTES)) {
                // 3. 强制关闭
                List<Runnable> unfinishedTasks = 
                    threadPool.shutdownNow();
                
                // 4. 处理未完成的任务
                handleUnfinishedTasks(unfinishedTasks);
                
                // 5. 再次等待
                if (!threadPool.awaitTermination(10, TimeUnit.MINUTES)) {
                    log.error("线程池无法正常关闭");
                }
            }
        } catch (InterruptedException e) {
            // 6. 重新中断
            threadPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    
    private void handleUnfinishedTasks(List<Runnable> tasks) {
        // 持久化未完成任务
        tasks.forEach(task -> {
            if (task instanceof PersistableTask) {
                taskPersistenceService.save((PersistableTask) task);
            }
        });
    }
}

八、最佳实践总结

8.1 线程池配置清单

配置项推荐值说明
corePoolSizeCPU核心数+1~核心数*2根据任务类型调整
maximumPoolSizecorePoolSize的2~4倍为突发流量预留
keepAliveTime30~60秒避免频繁创建线程
workQueueArrayBlockingQueue有界队列,防止OOM
threadFactory自定义命名方便问题排查
rejectedPolicyCallerRunsPolicy或自定义降级策略

8.2 线程池使用"七不要"

  1. 不要使用Executors的快捷方法
  2. 不要使用无界队列(LinkedBlockingQueue默认无界)
  3. 不要让任务抛出未捕获异常
  4. 不要在任务中无限循环而不检查中断
  5. 不要忘记关闭线程池
  6. 不要所有业务共用一个线程池
  7. 不要忽视线程池监控

8.3 推荐的线程池工具类

public class ThreadPoolUtils {
    
    private static final Map<String, ExecutorService> POOLS = 
        new ConcurrentHashMap<>();
    
    /**
     * 获取或创建线程池
     */
    public static ExecutorService getOrCreate(String poolName,
                                              Supplier<ExecutorService> creator) {
        return POOLS.computeIfAbsent(poolName, k -> creator.get());
    }
    
    /**
     * 安全执行任务
     */
    public static <T> CompletableFuture<T> safeSubmit(
            ExecutorService executor, 
            Callable<T> task) {
        CompletableFuture<T> future = new CompletableFuture<>();
        
        executor.submit(() -> {
            try {
                T result = task.call();
                future.complete(result);
            } catch (Throwable t) {
                future.completeExceptionally(t);
                log.error("任务执行失败", t);
            }
        });
        
        return future;
    }
    
    /**
     * 优雅关闭所有线程池
     */
    @PreDestroy
    public static void shutdownAll() {
        POOLS.forEach((name, pool) -> {
            try {
                pool.shutdown();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        });
    }
}

九、进阶:虚拟线程(Java 21+)

Java 21引入了虚拟线程,这是对线程模型的革命性改进:

// 创建虚拟线程
Thread virtualThread = Thread.ofVirtual()
    .name("virtual-thread-", 0)
    .start(() -> {
        // 任务逻辑
    });

// 使用虚拟线程的ExecutorService
ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();

// 性能对比:10万个任务
long start = System.currentTimeMillis();

// 传统线程池:可能创建数千个平台线程
try (ExecutorService executor = Executors.newFixedThreadPool(200)) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            Thread.sleep(100);
            return "done";
        });
    }
}

// 虚拟线程:轻松处理
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            Thread.sleep(100);
            return "done";
        });
    }
}

虚拟线程的优势:

  • 轻量级:内存开销约2KB(平台线程约1MB)
  • 高并发:轻松支持数百万个虚拟线程
  • 兼容性:完全兼容现有Thread API

总结

线程池是Java并发编程的核心组件,正确使用线程池需要:

  1. 理解原理:掌握ThreadPoolExecutor的工作机制
  2. 合理配置:根据业务场景选择合适的参数
  3. 监控预警:建立完善的监控体系
  4. 动态调整:根据负载情况动态调整参数
  5. 优雅处理:做好异常处理和优雅关闭

记住:没有万能的线程池配置,只有最适合你业务场景的配置。通过监控、分析和调整,不断优化你的线程池配置,才能构建出稳定高效的系统。

希望这篇文章能帮助你全面掌握线程池!如果有任何问题,欢迎随时讨论。