当前位置:首页 > 科技  > 软件

警惕!SpringBoot错误发布事件,造成死锁Deadlock

来源: 责编: 时间:2024-06-12 08:43:40 267观看
导读环境:SpringBoot3.2.51. 死锁复现1.1 自定义事件监听public class PackApplicationEvent extends ApplicationEvent { private String message ; public PackApplicationEvent(String message, Object source) {

环境:SpringBoot3.2.5Qon28资讯网——每日最新资讯28at.com

1. 死锁复现

1.1 自定义事件监听

public class PackApplicationEvent extends ApplicationEvent {  private String message ;  public PackApplicationEvent(String message, Object source) {    super(source) ;    this.message = message ;  }  public String getMessage() {    return message ;  }}

自定义事件,接收消息及相关数据Qon28资讯网——每日最新资讯28at.com

1.2 自定义事件监听

@Componentpublic class PackApplicationListener implements ApplicationListener<PackApplicationEvent> {  @Override  public void onApplicationEvent(PackApplicationEvent event) {    System.out.printf("接收到事件消息: %s, 数据: %s%n", event.getMessage(), event.getSource().toString()) ;    // TODO  }}

该事件监听器只打印了信息。Qon28资讯网——每日最新资讯28at.com

1.3 发布事件

@Componentpublic class EventProcessor {  public EventProcessor(ApplicationEventPublisher eventPublisher) {    Thread t = new Thread(() -> {      eventPublisher.publishEvent(new PackApplicationEvent("自定义事件", EventProcessor.this));    });    t.start() ;    try {      System.out.println("线程启动,等待执行完成...") ;      t.join() ;    } catch (InterruptedException e) {      System.err.printf("线程中断: %s, 错误: %s%n", Thread.currentThread().getName(), e.getMessage()) ;    }  }}

该Bean在构造函数中新启一个线程发布事件,同时通过join方法等待线程执行完成。Qon28资讯网——每日最新资讯28at.com

上面的程序运行后,发现输出了上面的打印内容后应用没有继续运行。打印整个线程栈(通过jstack命令查看),如下:Qon28资讯网——每日最新资讯28at.com

图片图片Qon28资讯网——每日最新资讯28at.com

根据线程信息,main线程在创建EventProcessor对象时,会先持有DefaultSingletonBeanRegistry.singletonObjects这个ConcurrentHashMap对象锁接着创建EventProcessor对象实例,在调用该对象的构造函数时,启动新的线程Thread-1,该线程发布事件同时通过join方法等待T1这个线程完成,在发布事件时Spring容器会获取所有的ApplicationListener,此时就会又创建PackApplicationListener对象,创建该对象同样要获取singletonObjects锁对象,这样就造成了死锁。Qon28资讯网——每日最新资讯28at.com

主线程

图片图片Qon28资讯网——每日最新资讯28at.com

主线程创建EventProcessor对象。Qon28资讯网——每日最新资讯28at.com

Thread-1线程

图片图片Qon28资讯网——每日最新资讯28at.com

Thread-1线程获取容器中的ApplicationListener类型的bean,该过程将执行到如下步骤:Qon28资讯网——每日最新资讯28at.com

图片图片Qon28资讯网——每日最新资讯28at.com

main线程持有singletonObjects锁,Thread-1线程又期望获取到该锁,但是main线程还要等待Thread-1线程执行完成。这死锁了。Qon28资讯网——每日最新资讯28at.com

以上是对死锁的复现及原因进行了分析,接下来进行问题的解决。Qon28资讯网——每日最新资讯28at.com

2. 解决问题

2.1 解决方式1

不要在构造函数中发布事件,而是应该在所有的单例对象都创建完后再执行,也就是实现SmartInitializingSingleton接口,该接口对应的回调方法会在所有的单例bean都创建完以后执行,这样就不会再出现deadlock问题。Qon28资讯网——每日最新资讯28at.com

@Componentpublic class EventProcessor implements SmartInitializingSingleton {  private final ApplicationEventPublisher eventPublisher ;  public EventProcessor(ApplicationEventPublisher eventPublisher) {    this.eventPublisher = eventPublisher ;  }  @Override  public void afterSingletonsInstantiated() {    Thread t = new Thread(() -> {      eventPublisher.publishEvent(new PackApplicationEvent("自定义事件", EventProcessor.this));    });    t.start() ;    try {      t.join() ;    } catch (InterruptedException e) {      System.err.printf("线程中断: %s, 错误: %s%n", Thread.currentThread().getName(), e.getMessage()) ;    }  }}

这样改造后容器能正常的启动,同时事件也正常的发布&监听。Qon28资讯网——每日最新资讯28at.com

afterSingletonsInstantiated方法的调用在如下:Qon28资讯网——每日最新资讯28at.com

public class DefaultListableBeanFactory {  public void preInstantiateSingletons() {    for (String beanName : beanNames) {      // 创建单例bean      getBean(beanName);    }    // 单例bean创建完成以后,执行afterSingletonsInstantiated回调方法    for (String beanName : beanNames) {      Object singletonInstance = getSingleton(beanName);      if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {        smartSingleton.afterSingletonsInstantiated();      }    }  }}

以上就不会在出现锁问题。Qon28资讯网——每日最新资讯28at.com

2.2 解决方式2

升级Spring版本到Spring6.2(目前并没有正式发布),你仍然可以使用6.2.0-SNAPSHOT版本,该版本通过多线程方式初始化Bean对象,这样就不会出现deadlock问题。Qon28资讯网——每日最新资讯28at.com


Qon28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-93206-0.html警惕!SpringBoot错误发布事件,造成死锁Deadlock

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 聊聊 Mybatis 动态 SQL

下一篇: 网易面试:SpringBoot如何开启虚拟线程?

标签:
  • 热门焦点
  • 三言两语说透设计模式的艺术-简单工厂模式

    一、写在前面工厂模式是最常见的一种创建型设计模式,通常说的工厂模式指的是工厂方法模式,是使用频率最高的工厂模式。简单工厂模式又称为静态工厂方法模式,不属于GoF 23种设计
  • CSS单标签实现转转logo

    转转品牌升级后更新了全新的Logo,今天我们用纯CSS来实现转转的新Logo,为了有一定的挑战性,这里我们只使用一个标签实现,将最大化的使用CSS能力完成Logo的绘制与动画效果。新logo
  • 企业采用CRM系统的11个好处

    客户关系管理(CRM)软件可以为企业提供很多的好处,从客户保留到提高生产力。  CRM软件用于企业收集客户互动,以改善客户体验和满意度。  CRM软件市场规模如今超过580
  • 三万字盘点 Spring 九大核心基础功能

    大家好,我是三友~~今天来跟大家聊一聊Spring的9大核心基础功能。话不多说,先上目录:图片友情提示,本文过长,建议收藏,嘿嘿嘿!一、资源管理资源管理是Spring的一个核心的基础功能,不
  • 一篇文章带你了解 CSS 属性选择器

    属性选择器对带有指定属性的 HTML 元素设置样式。可以为拥有指定属性的 HTML 元素设置样式,而不仅限于 class 和 id 属性。一、了解属性选择器CSS属性选择器提供了一种简单而
  • 三分钟白话RocketMQ系列—— 如何发送消息

    我们知道RocketMQ主要分为消息 生产、存储(消息堆积)、消费 三大块领域。那接下来,我们白话一下,RocketMQ是如何发送消息的,揭秘消息生产全过程。注意,如果白话中不小心提到相关代
  • 超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 引领旗舰级影像能力向中端机普及 OPPO K11 系列发布 1799 元起

    7月25日,OPPO正式发布K系列新品—— OPPO K11 。此次 K11 在中端手机市场长期被忽视的影像板块发力,突破性地搭载索尼 IMX890 旗舰大底主摄,支持 OIS
  • 联想的ThinkBook Plus下一版曝光,键盘旁边塞个平板

    ThinkBook Plus 是联想的一个特殊笔记本类别,它在封面放入了一块墨水屏,也给人留下了较为深刻的印象。据有人爆料,联想的下一款 ThinkBook Plus 可能更特殊,它
Top