温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

ExecutorService最佳实践

发布时间:2026-07-26 12:59:04 来源:亿速云 阅读:90 作者:小樊 栏目:编程语言

ExecutorService 是 Java 并发编程中管理线程池的核心工具。下面从设计原则、常见用法、配置建议、陷阱与最佳实践几个维度,给你一份实战级最佳实践总结


一、核心原则(先记住这 4 条)

  1. 不要手动创建线程,用线程池
  2. 线程池必须显式关闭
  3. 根据业务类型选择线程池
  4. 任务必须有界、可中断、可失败处理

二、推荐创建方式(不要用 Executors.*

❌ 不推荐(面试和实战都扣分)

ExecutorService executor = Executors.newFixedThreadPool(10);

原因:

  • 队列无界(LinkedBlockingQueue)→ 可能 OOM
  • 无法定制拒绝策略
  • 隐藏线程池真实行为

✅ 推荐:使用 ThreadPoolExecutor

ExecutorService executor = new ThreadPoolExecutor(
        4,                      // corePoolSize
        8,                      // maximumPoolSize
        60L, TimeUnit.SECONDS, // keepAliveTime
        new ArrayBlockingQueue<>(100), // 有界队列
        new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

三、线程池参数如何设置(核心问题)

1️⃣ 线程数怎么定?

任务类型 建议
CPU 密集型 CPU核心数 + 1
IO 密集型 CPU核心数 * 2 或更多
混合型 拆分线程池
int cores = Runtime.getRuntime().availableProcessors();

2️⃣ 队列大小怎么定?

必须有界

new ArrayBlockingQueue<>(500)

❌ 无界队列 = 风险


3️⃣ 拒绝策略怎么选?

策略 场景
AbortPolicy(默认) 重要任务,快速失败
CallerRunsPolicy 削峰、保护系统
自定义 记录日志 / 落盘 / 报警
new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        log.warn("task rejected");
    }
}

四、正确关闭线程池(非常重要)

✅ 标准关闭模板

executor.shutdown(); // 不再接受新任务

try {
    if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
        executor.shutdownNow(); // 强制关闭
    }
} catch (InterruptedException e) {
    executor.shutdownNow();
}

五、任务提交方式怎么选?

✅ 不同场景用不同方法

方法 是否关心结果
execute() 不关心
submit() 关心
invokeAll() 批量
invokeAny() 取最快

submit + Future 最佳实践

Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "ok";
});

try {
    String result = future.get(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    future.cancel(true);
}

永远给 get() 设置超时


六、异常处理(90% 的人会踩坑)

❌ 错误示例

executor.submit(() -> {
    throw new RuntimeException("error");
});

异常会被吞掉!


✅ 正确做法

方式 1:try-catch

executor.submit(() -> {
    try {
        doWork();
    } catch (Exception e) {
        log.error("task error", e);
    }
});

方式 2:Future.get()

Future<?> f = executor.submit(task);
f.get(); // 会抛异常

七、线程池命名(排查问题必备)

new ThreadFactory() {
    private final AtomicInteger count = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "biz-pool-" + count.getAndIncrement());
    }
}

或使用 Guava:

ThreadFactory factory = new ThreadFactoryBuilder()
        .setNameFormat("order-pool-%d")
        .build();

八、常见反模式(❌ 避免)

反模式 说明
一个 JVM 一个线程池 不同业务混在一起
任务无限阻塞 耗尽线程
提交后不管 丢 Future
线程池不关闭 应用无法优雅退出
Timer ScheduledExecutorService

九、典型业务线程池拆分示例

ExecutorService ioExecutor;     // IO 任务
ExecutorService cpuExecutor;    // 计算任务
ExecutorService asyncExecutor;  // 异步通知

不同 SLA 的任务,必须隔离线程池


十、一句话总结(面试版)

使用 ThreadPoolExecutor 创建线程池,
设置合理的核心线程数、有界队列和拒绝策略,
正确关闭线程池,
提交任务时处理异常并设置超时,
不同业务使用不同线程池。


如果你愿意,我可以:

  • ✅ 给你一份 生产级线程池配置模板
  • ✅ 帮你 分析现有线程池是否合理
  • ✅ 讲 ScheduledExecutorService 最佳实践
  • ✅ 对比 线程池 vs CompletableFuture

直接告诉我你的使用场景即可。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI