线程池常见错误使用场景
                           
天天向上
发布: 2025-04-26 18:57:06

原创
504 人浏览过

本章节针对线程池常见错误使用场景以及解法做了简单的总结,面试时如果讲到这里,绝对是高级选手水平


❌ 1. 使用 Executors 工厂方法创建线程池(极容易踩坑)

比如这些:

Executors.newFixedThreadPool()
Executors.newSingleThreadExecutor()
Executors.newCachedThreadPool()
Executors.newScheduledThreadPool()

问题:

  • 这些方法创建的线程池默认参数不合理
  • 比如:
  • newFixedThreadPool()newSingleThreadExecutor()阻塞队列是无限长 LinkedBlockingQueue任务堆积内存爆炸
  • newCachedThreadPool()线程数几乎无限扩展CPU、内存耗尽系统崩溃

正确做法:
推荐自己手动 new ThreadPoolExecutor(),合理设置参数。

比如👇

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,   // 核心线程数
    maximumPoolSize, // 最大线程数
    keepAliveTime,   // 空闲线程存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(queueCapacity), // 有界队列,防止堆积
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

❌ 2. 核心线程数、最大线程数、队列容量参数配置随便写

问题:

  • 配得太小 ➔ 线程池忙不过来,任务大量排队,系统响应慢。
  • 配得太大 ➔ 占满CPU、内存,导致系统其他线程饿死,系统挂掉。

正确做法:

  • 根据实际业务类型调优。
  • IO密集型任务(比如网络通信、数据库查询) ➔ 核心线程数可以设置为 CPU 核心数 × 2
  • CPU密集型任务(比如大量计算) ➔ 核心线程数可以设置为 CPU 核心数 + 1
  • 队列设置合理大小,不要太大也不要无限大。
  • 最大线程数 = 核心线程数 + 扩展保护线程数。

❌ 3. 不设置合理的拒绝策略

问题:

  • 默认拒绝策略是 AbortPolicy() ➔ 直接抛异常,很多人忽略了导致应用崩溃。
  • 其他拒绝策略(比如 CallerRunsPolicy)也有副作用,比如可能导致主线程也去执行任务,降低吞吐量。

正确做法:

  • 根据场景选择合理的拒绝策略:
  • 高实时性场景(秒杀系统) ➔ DiscardOldestPolicy(),丢弃旧任务
  • 非核心业务 ➔ DiscardPolicy(),直接丢弃
  • 核心业务 ➔ CallerRunsPolicy(),让调用方自己执行(要小心主线程被卡住)
  • 加上报警监控,线程池触发拒绝时一定要记录日志、报警。

❌ 4. 线程池数量太少,导致大量排队超时

问题:

  • 核心线程数太低,任务执行慢,用户请求超时,系统不可用。

正确做法:

  • 根据实际的任务量和性能测试数据,动态调节线程池大小,保证任务在可接受的时间内完成。

❌ 5. 多个业务共享一个线程池(线程池资源争抢)

问题:

  • 不同业务混在一个线程池里,容易互相影响。
  • 比如慢查询业务把整个线程池卡住,导致高优先级业务也执行不了。

正确做法:

  • 业务隔离:重要业务和次要业务使用不同的线程池,避免互相影响。

总结一版万能收尾(可直接在面试结尾用)

“线程池虽然能大幅提升系统性能和资源利用率,但如果参数配置不合理、拒绝策略处理不当或者业务没有隔离,就很容易导致系统雪崩。所以实际开发中要根据业务特点精细化调优线程池配置,并配合监控与报警机制,保障系统稳定运行。”


更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *