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

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

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

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

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

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

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

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

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

主线程

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

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

Thread-1线程

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

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

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

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

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

2. 解决问题

2.1 解决方式1

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

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

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

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

2.2 解决方式2

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


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

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

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

上一篇: 聊聊 Mybatis 动态 SQL

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

标签:
  • 热门焦点
  • Mate60手机壳曝光 致敬自己的经典设计

    8月3日消息,今天下午博主数码闲聊站带来了华为Mate60的第三方手机壳图,可以让我们在真机发布之前看看这款华为全新旗舰的大致轮廓。从曝光的图片看,Mate 60背后摄像头面积依然
  • .NET 程序的 GDI 句柄泄露的再反思

    一、背景1. 讲故事上个月我写过一篇 如何洞察 C# 程序的 GDI 句柄泄露 文章,当时用的是 GDIView + WinDbg 把问题搞定,前者用来定位泄露资源,后者用来定位泄露代码,后面有朋友反
  • Python异步IO编程的进程/线程通信实现

    这篇文章再讲3种方式,同时讲4中进程间通信的方式一、 Python 中线程间通信的实现方式共享变量共享变量是多个线程可以共同访问的变量。在Python中,可以使用threading模块中的L
  • 得物宠物生意「狂飙」,发力“它经济”

    作者|花花小萌主近日,得物宣布正式上线宠物鉴别,通过得物App内的&ldquo;在线鉴别&rdquo;,可找到鉴别宠物的选项。通过上传自家宠物的部位细节,就能收获拥有专业资质认证的得物鉴
  • 华为发布HarmonyOS 4:更好玩、更流畅、更安全

    在8月4日的华为开发者大会2023(HDC.Together)大会上,HarmonyOS 4正式发布。自2019年发布以来,HarmonyOS一直以用户为中心,经历四年多的发展HarmonyOS已
  • 2纳米决战2025

    集微网报道 从三强争霸到四雄逐鹿,2nm的厮杀声已然隐约传来。无论是老牌劲旅台积电、三星,还是誓言重回先进制程领先地位的英特尔,甚至初成立不久的新
  • 三星Galaxy Z Fold5今日亮相:厚度缩减但仍略显厚重

    据官方此前宣布,三星将于7月26日也就是今天在韩国首尔举办Unpacked活动,届时将带来带来包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy
  • 三星Galaxy Z Fold5官方渲染图曝光:13.4mm折叠厚度依旧感人

    据官方此前宣布,三星将于7月26日在韩国首尔举办Unpacked活动,届时将带来带来包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy Z Flip 5、
  • OPPO K11搭载高性能石墨散热系统:旗舰同款 性能凉爽释放

    日前OPPO官方宣布,将于7月25日14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖点就是将配备索尼
Top