验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

如何优化ExecutorService

阅读:274 来源:乙速云 作者:代码code

如何优化ExecutorService

ExecutorService 的优化通常围绕 线程数、任务类型、队列选择、拒绝策略、生命周期管理 等方面展开。下面从实战角度系统讲解优化思路,并给出示例。


一、先明确任务类型(优化前提)

任务类型 特点 推荐模型
CPU 密集型 计算多、阻塞少 线程数 ≈ CPU 核心数
IO 密集型 网络、DB、磁盘 线程数可以更大
混合型 既有计算又有 IO 拆分线程池

经验公式

  • CPU 密集型:N + 1
  • IO 密集型:N * 2N / (1 - 阻塞系数)
int cpuNums = Runtime.getRuntime().availableProcessors();

二、合理选择线程池类型

❌ 不推荐

Executors.newCachedThreadPool(); // 无线程数上限,容易 OOM
Executors.newFixedThreadPool(n); // 队列无界,风险高

✅ 推荐:手动创建 ThreadPoolExecutor

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    4,                      // corePoolSize
    8,                      // maximumPoolSize
    60L, TimeUnit.SECONDS, // 空闲线程存活时间
    new ArrayBlockingQueue<>(100), // 有界队列
    new ThreadFactoryBuilder()
        .setNameFormat("biz-pool-%d")
        .build(),
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

三、队列优化(非常关键)

队列选择建议

队列 适用场景 风险
ArrayBlockingQueue 有界、稳定 ✅ 推荐
LinkedBlockingQueue 默认无界 ❌ 易 OOM
SynchronousQueue 直接交付 适合短任务
PriorityBlockingQueue 有优先级 逻辑复杂

一定要用有界队列


四、拒绝策略优化

常见策略对比:

策略 行为 建议
AbortPolicy 抛异常 默认,不推荐
CallerRunsPolicy 调用线程执行 ✅ 推荐
DiscardPolicy 直接丢弃
DiscardOldestPolicy 丢弃最老

✅ 自定义拒绝策略(推荐)

new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 记录日志 / 降级 / 放入补偿队列
        log.warn("task rejected");
    }
}

五、线程池拆分(非常重要)

❌ 一个线程池干所有事
不同业务、不同任务类型拆分线程池

ExecutorService ioExecutor;
ExecutorService cpuExecutor;
ExecutorService asyncLogExecutor;

✅ 优点:

  • 避免互相阻塞
  • 更容易调优
  • 故障隔离

六、生命周期管理(避免泄漏)

正确关闭

executor.shutdown();
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
    executor.shutdownNow();
}

✅ 禁止:

  • 不 shutdown
  • 重复创建线程池

七、监控与调优(生产必备)

关键指标

executor.getActiveCount();
executor.getQueue().size();
executor.getCompletedTaskCount();

✅ 结合:

  • 日志
  • Prometheus / Micrometer

八、CompletableFuture + 线程池(现代写法)

CompletableFuture.supplyAsync(() -> doWork(), executor)
    .thenAccept(result -> handle(result));

✅ 优点:

  • 异步编排
  • 异常可控
  • 可读性高

九、常见错误总结

❌ 使用 Executors 默认方法
❌ 队列无界
❌ 线程数过大
❌ 一个线程池通吃所有任务
❌ 不监控、不关闭


十、推荐配置模板

ThreadPoolExecutor pool = new ThreadPoolExecutor(
    cpuNums * 2,
    cpuNums * 4,
    30,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(200),
    new ThreadFactoryBuilder().setNameFormat("io-pool-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

如果你愿意,可以告诉我:

  • 任务类型(IO / CPU / 混合)
  • QPS / 并发量
  • 是否 Web / 后台任务

我可以帮你 直接给出最优线程池参数配置

分享到:
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
相关文章
{{ v.title }}
{{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
你可能感兴趣
推荐阅读 更多>