没想到,这么简单的线程池用法,深藏这么多坑!

又又又踩坑了

生产有个对账系统,每天需要从渠道端下载对账文件,然后开始日终对账。这个系统已经运行了很久,前两天突然收到短信预警,没有获取渠道端对账文件。

ps:对账系统详细实现方式:对账系统设计与实现

本以为又是渠道端搞事情,上去一排查才发现,所有下载任务都被阻塞了。再进一步排查源码,才发现自己一直用错了线程池某个方法。

由于线程创建比较昂贵,正式项目中我们都会使用线程池执行异步任务。线程池,使用池化技术保存线程对象,使用的时候直接取出来,用完归还以便使用。

虽然线程池的使用非常方法非常简单,但是越简单,越容易踩坑。细数一下,这些年来因为线程池导致生产事故也有好几起。

所以今天,小黑哥就针对线程池的话题,给大家演示一下怎么使用线程池才会踩坑。

希望大家看完,可以完美避开这些坑~

先赞后看,养成习惯。微信搜索「程序通事」,关注就完事了!

慎用 Executors 组件

Java 从 JDK1.5 开始提供线程池的实现类,我们只需要在构造函数内传入相关参数,就可以创建一个线程池。

不过线程池的构造函数可以说非常复杂,就算最简单的那个构造函数,也需要传入 5 个参数。这对于新手来说,非常不方便哇。

也许 JDK 开发者也考虑到这个问题,所以非常贴心给我们提供一个工具类 Executors,用来快捷创建创建线程池。

虽然这个工具类使用真的非常方便,可以少写很多代码,但是小黑哥还是建议生产系统还是老老实实手动创建线程池,慎用Executors,尤其是工具类中两个方法 Executors#newFixedThreadPoolExecutors#newCachedThreadPool

如果你图了方便使用上述方法创建了线程池,那就是一颗定时炸弹,说不准那一天生产系统就会💥。

我们来看两个🌰,看下这个这两个方法会有什么问题。

假设我们有个应用有个批量接口,每次请求将会下载 100w 个文件,这里我们使用 Executors#newFixedThreadPool批量下载。

下面方法中,我们随机休眠,模拟真实下载耗时。

为了快速复现问题,调整 JVM 参数为 -Xmx128m -Xms128m

private ExecutorService threadPool = Executors.newFixedThreadPool(10);

/**
 * 批量下载对账文件
 *
 * @return
 */
@RequestMapping("/batchDownload")
public String batchDownload() {
   
    
    // 模拟下载 100w 个文件
    for (int i = 0; i < 1000000; i++) {
   
        threadPool.execute(() -> {
   
            // 随机休眠,模拟下载耗时
            Random random = new Random();
            try {
   
                TimeUnit.SECONDS.sleep(random.nextInt(100));
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        });
    }

    return "process";
}

程序运行之后,多请求几次这个批量下载方法,程序很快就会 OOM

查看 Executors#newFixedThreadPool

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值