一、创建线程有几种方式在 Java 里本质只有一条路径创建Thread对象并start()。但写法/封装常见有 4 种方式说明1. 继承Thread重写run()再new MyThread().start()2. 实现Runnablenew Thread(runnable).start()推荐解耦任务和线程3. 实现Callable Future有返回值、可抛受检异常配合ExecutorService4. 线程池Executors工厂 或 自定义ThreadPoolExecutor生产环境推荐补充Runnable无返回值void run()CallableV有返回值配合FutureTask或线程池的submit()// 1. 继承 Thread不推荐扩展性差 class MyThread extends Thread { public void run() { /* ... */ } } // 2. Runnable常用 new Thread(() - System.out.println(task)).start(); // 3. Callable FutureTaskString future new FutureTask(() - result); new Thread(future).start(); // 4. 线程池生产推荐 ExecutorService pool new ThreadPoolExecutor(...); pool.submit(() - result);二、自定义线程池核心参数ThreadPoolExecutor生产环境不要用Executors.newFixedThreadPool()等工厂方法队列可能无界、线程数固定不合理应显式new ThreadPoolExecutor(...)new ThreadPoolExecutor( corePoolSize, // 核心线程数 maximumPoolSize, // 最大线程数 keepAliveTime, // 非核心线程空闲存活时间 unit, // 时间单位 workQueue, // 任务队列 threadFactory, // 线程工厂命名、优先级等 handler // 拒绝策略 );各参数含义参数含义corePoolSize核心线程数。池子里常驻的线程数即使空闲也不会被回收除非allowCoreThreadTimeOuttruemaximumPoolSize最大线程数。队列满后才会在核心线程之外再创建线程总数不超过此值keepAliveTime超过corePoolSize的那部分线程空闲多久后销毁workQueue任务等待队列。常见ArrayBlockingQueue有界、LinkedBlockingQueue可设容量、SynchronousQueue不存任务直接交给线程threadFactory创建线程时用便于打日志、设线程名如biz-pool-%dhandler队列满且线程数已达maximumPoolSize时的拒绝策略任务提交流程简化提交任务→ 当前线程数 corePoolSize→ 新建核心线程执行→ 否则尝试入队→ 队列满 且 线程数 maximumPoolSize→ 新建非核心线程执行→ 否则走拒绝策略四种拒绝策略策略行为AbortPolicy默认抛RejectedExecutionExceptionCallerRunsPolicy调用者线程自己跑起到背压作用常用DiscardPolicy静默丢弃DiscardOldestPolicy丢弃队列里最老的任务再提交新的三、如何根据机器和业务设置参数先分清任务类型类型特点线程主要在做什么CPU 密集型算力、编解码、加解密、复杂计算占满 CPU 时间片IO 密集型查 DB、HTTP、读盘、RPC大量时间在等 IOCPU 空闲CPU 密集型目标线程数 ≈ CPU 核心数避免过多线程导致上下文切换。经验公式线程数 ≈ N_cpu 1或线程数 ≈ N_cpuN_cpuRuntime.getRuntime().availableProcessors()或容器里的 CPU quota1是为了某个线程偶尔阻塞如缺页时其它核仍能跑满示例8 核int cpu Runtime.getRuntime().availableProcessors(); int core cpu; int max cpu 1; // 队列可以小一点有界队列 CallerRunsPolicyIO 密集型目标线程在等 IO 时其它线程继续用 CPU。常见估算假设线程约%wait时间在等待 IO线程数 ≈ N_cpu × (1 W/C)W等待时间IOC计算时间若 IO 占 90%N_cpu × (1 9) ≈ 10 × N_cpu更实用的经验范围无精确 profiling 时线程数 ≈ N_cpu × 2 ~ N_cpu × (1 平均阻塞时间/平均CPU时间)例如 8 核、大量 HTTP/DBint cpu Runtime.getRuntime().availableProcessors(); int core cpu * 2; int max cpu * 4; // 上限要设并结合压测调注意不是越大越好线程过多会增加内存、调度开销还要受连接池、DB 最大连接数等限制。四、实操建议结合机器1. 先搞清楚“机器”是多少核int n Runtime.getRuntime().availableProcessors();物理机看 CPU 核心数Docker/K8s看 CPU limitavailableProcessors通常已按 limit 返回混合部署按该进程实际能用的核数算不是整机核数2. 分池不要一个大池包天下CPU 池计算、报表、加密 → 小池≈ N_cpuIO 池HTTP、DB、MQ → 大池≈ N_cpu × 2~4压测微调3. 队列要有界new ArrayBlockingQueue(1000) // 或 LinkedBlockingQueue(capacity)无界队列会导致任务堆积、OOM且maximumPoolSize几乎用不上。4. 用压测定参而不是只套公式观察指标说明CPU 利用率CPU 任务池长期 100% 且队列积压 → 可能算力不够或池太小队列长度 / 拒绝次数频繁拒绝 → 加大池或优化下游响应时间 P99IO 池线程够但 RT 高 → 可能是 DB/网络瓶颈不是加线程能解决上下文切换线程过多时vmstat里 cs 很高5. 快速对照表场景corePoolSizemaximumPoolSize队列CPU 密集N_cpuN_cpu 或 N_cpu1较小有界IO 密集N_cpu×2N_cpu×4压测调中等有界混合型拆成两个池分别配置五、一句话总结创建线程继承 Thread、Runnable、Callable、线程池生产用自定义ThreadPoolExecutor。核心参数core常驻、max上限、keepAlive回收非核心线程、有界queue、handler拒绝策略。CPU 密集线程数 ≈ CPU 核数IO 密集线程数 ≈ 核数 × (2~4 或按 W/C 估算)最终以压测和下游连接数为上限。
Java线程池参数设置与优化指南
发布时间:2026/7/2 1:38:47
一、创建线程有几种方式在 Java 里本质只有一条路径创建Thread对象并start()。但写法/封装常见有 4 种方式说明1. 继承Thread重写run()再new MyThread().start()2. 实现Runnablenew Thread(runnable).start()推荐解耦任务和线程3. 实现Callable Future有返回值、可抛受检异常配合ExecutorService4. 线程池Executors工厂 或 自定义ThreadPoolExecutor生产环境推荐补充Runnable无返回值void run()CallableV有返回值配合FutureTask或线程池的submit()// 1. 继承 Thread不推荐扩展性差 class MyThread extends Thread { public void run() { /* ... */ } } // 2. Runnable常用 new Thread(() - System.out.println(task)).start(); // 3. Callable FutureTaskString future new FutureTask(() - result); new Thread(future).start(); // 4. 线程池生产推荐 ExecutorService pool new ThreadPoolExecutor(...); pool.submit(() - result);二、自定义线程池核心参数ThreadPoolExecutor生产环境不要用Executors.newFixedThreadPool()等工厂方法队列可能无界、线程数固定不合理应显式new ThreadPoolExecutor(...)new ThreadPoolExecutor( corePoolSize, // 核心线程数 maximumPoolSize, // 最大线程数 keepAliveTime, // 非核心线程空闲存活时间 unit, // 时间单位 workQueue, // 任务队列 threadFactory, // 线程工厂命名、优先级等 handler // 拒绝策略 );各参数含义参数含义corePoolSize核心线程数。池子里常驻的线程数即使空闲也不会被回收除非allowCoreThreadTimeOuttruemaximumPoolSize最大线程数。队列满后才会在核心线程之外再创建线程总数不超过此值keepAliveTime超过corePoolSize的那部分线程空闲多久后销毁workQueue任务等待队列。常见ArrayBlockingQueue有界、LinkedBlockingQueue可设容量、SynchronousQueue不存任务直接交给线程threadFactory创建线程时用便于打日志、设线程名如biz-pool-%dhandler队列满且线程数已达maximumPoolSize时的拒绝策略任务提交流程简化提交任务→ 当前线程数 corePoolSize→ 新建核心线程执行→ 否则尝试入队→ 队列满 且 线程数 maximumPoolSize→ 新建非核心线程执行→ 否则走拒绝策略四种拒绝策略策略行为AbortPolicy默认抛RejectedExecutionExceptionCallerRunsPolicy调用者线程自己跑起到背压作用常用DiscardPolicy静默丢弃DiscardOldestPolicy丢弃队列里最老的任务再提交新的三、如何根据机器和业务设置参数先分清任务类型类型特点线程主要在做什么CPU 密集型算力、编解码、加解密、复杂计算占满 CPU 时间片IO 密集型查 DB、HTTP、读盘、RPC大量时间在等 IOCPU 空闲CPU 密集型目标线程数 ≈ CPU 核心数避免过多线程导致上下文切换。经验公式线程数 ≈ N_cpu 1或线程数 ≈ N_cpuN_cpuRuntime.getRuntime().availableProcessors()或容器里的 CPU quota1是为了某个线程偶尔阻塞如缺页时其它核仍能跑满示例8 核int cpu Runtime.getRuntime().availableProcessors(); int core cpu; int max cpu 1; // 队列可以小一点有界队列 CallerRunsPolicyIO 密集型目标线程在等 IO 时其它线程继续用 CPU。常见估算假设线程约%wait时间在等待 IO线程数 ≈ N_cpu × (1 W/C)W等待时间IOC计算时间若 IO 占 90%N_cpu × (1 9) ≈ 10 × N_cpu更实用的经验范围无精确 profiling 时线程数 ≈ N_cpu × 2 ~ N_cpu × (1 平均阻塞时间/平均CPU时间)例如 8 核、大量 HTTP/DBint cpu Runtime.getRuntime().availableProcessors(); int core cpu * 2; int max cpu * 4; // 上限要设并结合压测调注意不是越大越好线程过多会增加内存、调度开销还要受连接池、DB 最大连接数等限制。四、实操建议结合机器1. 先搞清楚“机器”是多少核int n Runtime.getRuntime().availableProcessors();物理机看 CPU 核心数Docker/K8s看 CPU limitavailableProcessors通常已按 limit 返回混合部署按该进程实际能用的核数算不是整机核数2. 分池不要一个大池包天下CPU 池计算、报表、加密 → 小池≈ N_cpuIO 池HTTP、DB、MQ → 大池≈ N_cpu × 2~4压测微调3. 队列要有界new ArrayBlockingQueue(1000) // 或 LinkedBlockingQueue(capacity)无界队列会导致任务堆积、OOM且maximumPoolSize几乎用不上。4. 用压测定参而不是只套公式观察指标说明CPU 利用率CPU 任务池长期 100% 且队列积压 → 可能算力不够或池太小队列长度 / 拒绝次数频繁拒绝 → 加大池或优化下游响应时间 P99IO 池线程够但 RT 高 → 可能是 DB/网络瓶颈不是加线程能解决上下文切换线程过多时vmstat里 cs 很高5. 快速对照表场景corePoolSizemaximumPoolSize队列CPU 密集N_cpuN_cpu 或 N_cpu1较小有界IO 密集N_cpu×2N_cpu×4压测调中等有界混合型拆成两个池分别配置五、一句话总结创建线程继承 Thread、Runnable、Callable、线程池生产用自定义ThreadPoolExecutor。核心参数core常驻、max上限、keepAlive回收非核心线程、有界queue、handler拒绝策略。CPU 密集线程数 ≈ CPU 核数IO 密集线程数 ≈ 核数 × (2~4 或按 W/C 估算)最终以压测和下游连接数为上限。