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

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

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

环境:SpringBoot3.2.5t8a28资讯网——每日最新资讯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 ;  }}

自定义事件,接收消息及相关数据t8a28资讯网——每日最新资讯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  }}

该事件监听器只打印了信息。t8a28资讯网——每日最新资讯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方法等待线程执行完成。t8a28资讯网——每日最新资讯28at.com

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

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

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

主线程

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

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

Thread-1线程

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

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

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

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

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

2. 解决问题

2.1 解决方式1

不要在构造函数中发布事件,而是应该在所有的单例对象都创建完后再执行,也就是实现SmartInitializingSingleton接口,该接口对应的回调方法会在所有的单例bean都创建完以后执行,这样就不会再出现deadlock问题。t8a28资讯网——每日最新资讯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()) ;    }  }}

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

afterSingletonsInstantiated方法的调用在如下:t8a28资讯网——每日最新资讯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();      }    }  }}

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

2.2 解决方式2

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


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

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

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

上一篇: 聊聊 Mybatis 动态 SQL

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

标签:
  • 热门焦点
  • K60至尊版刚预热 一加Ace2 Pro正面硬刚

    Redmi这边刚如火如荼的宣传了K60 Ultra的各种技术和硬件配置,作为竞品的一加也坐不住了。一加中国区总裁李杰发布了两条微博,表示在自家的一加Ace2上早就已经采用了和PixelWo
  • Mate60手机壳曝光 致敬自己的经典设计

    8月3日消息,今天下午博主数码闲聊站带来了华为Mate60的第三方手机壳图,可以让我们在真机发布之前看看这款华为全新旗舰的大致轮廓。从曝光的图片看,Mate 60背后摄像头面积依然
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • 7月安卓手机好评榜:三星S23Ultra好评率第一

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年7月1日至7月31日,仅限国内市场。第一名:三星Galaxy S23 Ultra好评率:95.71%在即将迎来新
  • CSS单标签实现转转logo

    转转品牌升级后更新了全新的Logo,今天我们用纯CSS来实现转转的新Logo,为了有一定的挑战性,这里我们只使用一个标签实现,将最大化的使用CSS能力完成Logo的绘制与动画效果。新logo
  • SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • 谷歌KDD'23工作:如何提升推荐系统Ranking模型训练稳定性

    谷歌在KDD 2023发表了一篇工作,探索了推荐系统ranking模型的训练稳定性问题,分析了造成训练稳定性存在问题的潜在原因,以及现有的一些提升模型稳定性方法的不足,并提出了一种新
  • 猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • Counterpoint :OPPO双旗舰战略全面落地 高端产品销量增长22%

    2023年6月30日,全球行业分析机构Counterpoint Research发布的《中国智能手机高端市场白皮书》显示,中国智能手机品牌正在寻求高质量发展,中国高端智能
Top