线程池的死锁事件

欢迎大家关注公众号【离心计划】,更多高质量的教程系列,一起离开地球表面,逃离舒适圈

背景

事情是这样的,手上的一个项目使用了线程池来处理大量的异步任务,起初的目的是为了并行处理减少耗时而已,但是前天流量由于其他同事的离线job陡增了数十倍,我们先不讨论这个规范性问题。由于流量尖峰,服务出现了超时,引发上游服务拿不到结果,而前端对他们服务端的超时配置不合理,直接引发了白页,听起来很离谱

,大致的问题请求流程如下:

排查

出现超时但是服务的CPU占用却不高,没有触发HPA(动态扩容),我们查看了集群某台机器的线程情况大量线程TimeWait,并且定位到了问题代码,首先我们看我们的线程池定义(示例)

ThreadPoolExecutor tpe = new ThreadPoolExecutor(coreNum,coreNum*2,                100L, TimeUnit.MILLISECONDS,                new LinkedBlockingQueue<>(),namedThreadFactory);

问题如下:

  • 队列长度没限制

  • 拒绝策略走到最好添加日志或者告警

然后最关键的地方,这个项目里面存在这样的父子任务关系:​​​​​​​

/**
     * 父任务,里面异步执行子任务
     */
    static class FatherTaskTwo implements Callable<String> {
        @Override
        public String call() throws Exception {
            SonTask sonTask = new SonTask();
            Future<String> future = tpe.submit(sonTask);
            return future.get();
        }
    }
    /**
     * 子任务
     */
    static class SonTask implements Callable<String> {
        @Override
        public String call() throws Exception {
            //处理一些业务逻辑
            return null;
        }
    }
    public static void main(String[] args) throws Exception {
        FatherTaskTwo fatherTaskTwo = new FatherTaskTwo();
        Future<String> future = tpe.submit(fatherTaskTwo);
        //主线程添加超时时间get阻塞
        future.get(1000L, TimeUnit.MILLISECONDS);
    }

这里有几个问题:
1. 父任务get子任务的结果没有设置超时时间

2. 父子任务共用了一个线程池

这会导致什么问题呢?就是今天的关键:死锁

我们假设最大有四个线程同时处理,我们正常的情况可能像这样:

ok,为什么这是正常情况,可以想象下子任务1和子任务2会被很快执行完,那么子任务3和4就会马上被执行,但是父任务3和父任务4是要等到子任务3和子任务4执行完才会返回结果的,因为父子有依赖关系,并且父任务中get子任务是 没有设置超时时间,但是这种情况还好,因为子任务34在子任务12执行完后可以拿到线程资源去执行掉,进而把父任务34也消化掉,但是如果是下面这样的情况:

那么就出现了四个父任务都无法消化掉,原因在于:

  1. 父任务都依赖自己的子任务

  2. 父任务没有超时时间,会一直阻塞,打破不了死锁的条件

这就形成了死锁,由于阻塞挂起线程所以CPU也不会由于线程池的问题而升高

结论

虽然整条事件链路有许多不合理的地方,比如服务之间的降级、Online/Offline的边界划分等等,但是我们主要关注应用内部的问题,主要是错误使用线程池导致的死锁,那么我们的解决方案在不同角度有大致几种:

  1. 线程池隔离,有依赖关系的线程在不同池内处理

  2. 一定要设置get超时时间

  3. 非必要不要拆这么多层级任务,尽量扁平化

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值