Skip to content

线程池调优

"线程池参数怎么设?我的服务器是 16 核,应该设多少线程?"

这是被问得最多的问题之一。

答案取决于任务类型。

任务类型决定线程数

CPU 密集型 vs IO 密集型

CPU 密集型:任务主要是计算,极少等待
示例:复杂数学运算、数据加密、图像处理
特点:线程大部分时间在 CPU 上运算

IO 密集型:任务主要是等待 IO
示例:数据库查询、文件读写、HTTP 请求
特点:线程大部分时间在等待

线程数计算公式

公式:
线程数 = CPU核心数 * CPU利用率 * (1 + 等待时间/计算时间)

对于:

  • CPU 密集型:等待时间 ≈ 0,所以线程数 ≈ CPU核心数
  • IO 密集型:等待时间 >> 计算时间,所以线程数 > CPU核心数

代码演示

CPU 密集型配置

java
public class CPUIntensiveConfig {

    public static void main(String[] args) {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        System.out.println("CPU 核心数: " + cpuCores);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            cpuCores,              // 核心线程数
            cpuCores,              // 最大线程数
            0L, TimeUnit.MILLISECONDS,  // 不需要回收
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.AbortPolicy()
        );

        // 提交计算任务
        for (int i = 0; i < 20; i++) {
            executor.submit(() -> {
                long sum = 0;
                for (int j = 0; j < 1_000_000; j++) {
                    sum += Math.sqrt(j);
                }
            });
        }

        executor.shutdown();
    }
}

IO 密集型配置

java
public class IOIntensiveConfig {

    public static void main(String[] args) {
        int cpuCores = Runtime.getRuntime().availableProcessors();

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            cpuCores * 2,            // IO 密集型可以多一些
            cpuCores * 4,
            60L, TimeUnit.SECONDS,   // 较长空闲时间
            new LinkedBlockingQueue<>(1000),
            new ThreadPoolExecutor.AbortPolicy()
        );

        // 提交 IO 任务
        for (int i = 0; i < 50; i++) {
            executor.submit(() -> {
                try {
                    Thread.sleep(100);  // 模拟 IO 等待
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

混合任务配置

java
public class MixedTaskConfig {

    public static void main(String[] args) {
        int cpuCores = Runtime.getRuntime().availableProcessors();

        // CPU 密集型任务用 CPU 线程池
        ExecutorService cpuPool = new ThreadPoolExecutor(
            cpuCores, cpuCores,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(100)
        );

        // IO 密集型任务用 IO 线程池
        ExecutorService ioPool = new ThreadPoolExecutor(
            cpuCores * 2, cpuCores * 4,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(500)
        );

        cpuPool.submit(() -> {
            long sum = 0;
            for (int i = 0; i < 1_000_000; i++) {
                sum += Math.sqrt(i);
            }
        });

        ioPool.submit(() -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        cpuPool.shutdown();
        ioPool.shutdown();
    }
}

动态调整线程池

java
public class DynamicThreadPool {

    private final ThreadPoolExecutor executor;

    public DynamicThreadPool() {
        this.executor = new ThreadPoolExecutor(
            5, 20,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100)
        );
    }

    public void setCorePoolSize(int size) {
        executor.setCorePoolSize(size);
    }

    public void setMaximumPoolSize(int size) {
        executor.setMaximumPoolSize(size);
    }

    public void prestartAllCoreThreads() {
        executor.prestartAllCoreThreads();
    }

    public void allowCoreThreadTimeOut(boolean allow) {
        executor.allowCoreThreadTimeOut(allow);
    }

    public static void main(String[] args) {
        DynamicThreadPool pool = new DynamicThreadPool();

        pool.prestartAllCoreThreads();       // 预热
        pool.setCorePoolSize(10);            // 动态调整
        pool.setMaximumPoolSize(30);
        pool.allowCoreThreadTimeOut(true);   // 允许核心线程超时回收
    }
}

监控与调优

java
public class ThreadPoolTuningDemo {

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10)
        );

        // 定时监控
        ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
        monitor.scheduleAtFixedRate(() -> {
            printStats(executor);
        }, 0, 2, TimeUnit.SECONDS);

        // 提交任务
        for (int i = 0; i < 30; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(500 + new Random().nextInt(500));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        Thread.sleep(10000);
        monitor.shutdown();
        executor.shutdown();
    }

    private static void printStats(ThreadPoolExecutor executor) {
        System.out.println("=== 线程池状态 ===");
        System.out.println("核心线程数: " + executor.getCorePoolSize());
        System.out.println("最大线程数: " + executor.getMaximumPoolSize());
        System.out.println("当前线程数: " + executor.getPoolSize());
        System.out.println("活跃线程数: " + executor.getActiveCount());
        System.out.println("已完成任务: " + executor.getCompletedTaskCount());
        System.out.println("队列大小: " + executor.getQueue().size());
        System.out.println("队列容量: " + executor.getQueue().remainingCapacity());
        System.out.println();
    }
}

经验值参考

任务类型线程数公式说明
CPU 密集型CPU核心数CPU核心数 + 1 也常见
IO 密集型CPU核心数 * 2或更多,取决于 IO 等待比例
混合型分开配置CPU 任务和 IO 任务用不同线程池

注意事项

  1. 线程数不是越大越好:线程创建、切换、销毁都有开销
  2. 队列大小要平衡:太小容易拒绝,太大可能 OOM
  3. 监控是调优的基础:根据实际数据调整
  4. 考虑任务特性:执行时间长短、阻塞程度都会影响最优配置
  5. 避免过度优化:先让代码正确运行,再根据数据优化

要点回顾

任务类型 → 线程数公式

CPU 密集型:
  线程数 = CPU核心数
  队列 = 有界队列

IO 密集型:
  线程数 = CPU核心数 * 2(甚至更多)
  队列 = 有界队列
  keepAliveTime = 较长

混合型:
  分开配置,用不同线程池处理不同类型任务

基于 VitePress 构建