栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

记一次程序死锁问题排查

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

记一次程序死锁问题排查

死锁四个条件
  1. 互斥条件:一个资源每次只能被一个线程使用
  2. 占有且等待:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不可强行占有:线程已获得的资源,在未使用完时,不能强行剥夺
  4. 循环等待条件:若干线程之间形成一种头尾相连的循环等待资源关系
问题排查

程序在启动过程中出现卡死现象,程序启动日志卡住,消费者消费到消息日志也是卡住的,凭感觉应该是程序启动的时候出现问题导致主线程挂起,于是执行下jstack命令查看下线程快照,得到下面的结果

Found one Java-level deadlock:
=============================
"ConsumeMessageThread_18":
  waiting to lock monitor 0x000000002299e428 (object 0x00000006c4a374f0, a java.lang.String),
  which is held by "ConsumeMessageThread_8"
"ConsumeMessageThread_8":
  waiting to lock monitor 0x000000002baa6188 (object 0x00000006c3ab1578, a java.util.concurrent.ConcurrentHashMap),
  which is held by "main"
"main":
  waiting to lock monitor 0x000000002299e428 (object 0x00000006c4a374f0, a java.lang.String),
  which is held by "ConsumeMessageThread_8"

看来之前的猜想是正确的,由于出现了死锁所以出现程序日志出现卡住的情况,但是是什么原因导致死锁呢?接着查看死锁线程的堆栈信息

"main":
        at org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.getBean(GenericScope.java:388)
        - waiting to lock <0x00000006c4a374f0> (a java.lang.String)
        at org.springframework.cloud.context.scope.GenericScope.get(GenericScope.java:186)
        - locked <0x00000006c3ab1578> (a java.util.concurrent.ConcurrentHashMap)
"ConsumeMessageThread_8":
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:216)
        - waiting to lock <0x00000006c3ab1578> (a java.util.concurrent.ConcurrentHashMap)
        - locked <0x00000006c4a374f0> (a java.lang.String)
        at org.springframework.cloud.context.scope.GenericScope.get(GenericScope.java:186)

可以看到主线程持有0x00000006c3ab1578并等待获取0x00000006c4a374f0在org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.getBean这个位置;消费者线程8等待获取0x00000006c3ab1578并持有0x00000006c4a374f0在DefaultSingletonBeanRegistry.getSingleton这个位置;这样就形成了一个死锁。

有上面的信息可以大致得出主线程spring context未加载完成时,有消费线程需要从spring context中获取bean,从而导致死锁。正常的来说在spring context未加载完成时,rocketMQ消费线程应该不会工作的。问题原因只能着手去业务代码中发现问题了

    @Bean
    public DefaultMQPushConsumer defaultMQPushConsumer() {
        .......
        .......
        //通过依赖注入的Fegin调用
        consumer.start();
    }

在声明消费者Bean的时候直接调用了start()方法,这样就会导致Bean初始化的时候就会启动消费。

快速解决方案
public class Test implements ApplicationRunner {

    @Autowired
    DefaultMQPushConsumer consumer;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        consumer.start();
    }
}

在容器启动完成之后再调用start()方法,这样就避免了spring context还未加载完成就去获取Bean。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/282314.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号