怎样修改静态公司网站页面电话,电影网站html代码,郴州网站建设找哪家公司,网站建设开发服务费税率现象描述#xff1a;Spring Boot项目#xff0c;启动的时候卡住了#xff0c;一直卡在那里不动#xff0c;没有报错#xff0c;也没有日志输出 但是#xff0c;奇怪的是#xff0c;本地可以正常启动 好吧#xff0c;姑且先不深究为什么本地可以启动而部署到服务器上就无…现象描述Spring Boot项目启动的时候卡住了一直卡在那里不动没有报错也没有日志输出 但是奇怪的是本地可以正常启动 好吧姑且先不深究为什么本地可以启动而部署到服务器上就无法启动的问题这个不是重点重点是怎么让它启动起来。PS我猜测可能是环境不同造成的包括操作系统不同和JDK版本不同
遇到这种情况我先用jstack查看堆栈情况果然发现了死锁 拿到jstack的完整信息然后仔细排查看不懂的话也可以借助工具 分析了每个被阻塞的线程之后发现main线程和timeoutChecker_1_1互相等待对方持有的锁从而形成了死锁
可以通过 jconsole 和 jvisualvm 查看 需要注意如果是查看远程进程则需要加一些启动参数
-Dcom.sun.management.jmxremote启用JMX-Dcom.sun.management.jmxremote.port端口号指定JMX远程连接的端口号-Dcom.sun.management.jmxremote.authenticatefalse禁用JMX远程连接的认证-Dcom.sun.management.jmxremote.sslfalse禁用JMX远程连接的SSL加密 于是我又重启启动
java -jar -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port9099 -Dcom.sun.management.jmxremote.authenticatefalse -Dcom.sun.management.jmxremote.sslfalse app.jar
通过jps或者ps命令查找应用的pid 用jvisualvm查看也可以不再赘述结果都是一样的 好了工具介绍到此为止下面重点看代码 main线程持有0x00000000c07a33d8这个对象的锁同时它还需要0x00000000ff295ca8对象的锁而timeoutChecker_1_1线程正好相反于是死锁了
main线程很好理解就是我们这个SpringBoot应用的主线程但是timeoutChecker_1_1线程是哪儿来的呢通过分析发现它来自Seata
对了该项目中Spring Boot版本是2.6.6Seata版本是1.4.2
找到timeoutChecker的出处了 延迟60秒启动定时任务每隔10秒执行一次调用io.seata.core.rpc.netty.NettyClientChannelManager#reconnect() 记住这一行首先调用RegistryFactory.getInstance()获取一个RegistryService然后调用RegistryService对象的lookup()方法 接着看1.4.2 最重要的是 EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration); 所以ExtConfigurationProvider 是 SpringBootConfigurationProvider 回到seata-1.4.2可以看到这里调用了applicationContext.getBean()于是DefaultListableBeanFactory.getBean() 可以看到getSingletonFactoryBeanForTypeCheck()方法里对singletonObjects加了同步锁
凡是通过DefaultSingletonBeanRegistry#getSingleton()获取单例Bean的都会先对singletonObjects加锁 接下来看lookup 可以看到NacosRegistryServiceImpl的lookup()这里也加了锁。另外getNamingProperties()的时候由于再次用到了ConfigurationFactory.CURRENT_FILE_INSTANCE所以又到了SpringBootConfigurationProvider#provide()
至此Seata整个定时任务启动的主要逻辑我们都梳理完了几处加锁的也都找到了 这些加锁的地方也就是容易出现死锁的地方
死锁是由于加锁顺序不一致造成的
下面看main线程启动
由于SeataDataSourceBeanPostProcessor实现了BeanPostProcessor接口所以在创建容器之后会回调其postProcessAfterInitialization()方法 所以最终还是调NettyClientChannelManager#reconnect() Spring启动的时候去创建Spring容器后面就是Spring那一套
ConfigurableApplicationContext#refresh()
ServletWebServerApplicationContext#refresh() 不再赘述
由于需要注入依赖所以这个过程中肯定会多次调用 AbstractBeanFactory.getBean()
前面我们讲过DefaultSingletonBeanRegistry.getSingleton() 时是加了锁的。因此main线程很有可能会先持有该锁当初始化到Seata的时候又要获取该锁于是出现了锁争用。 由于两个线程对同一资源的加锁顺序不一致导致死锁。
由于timeoutChecker是定时任务每隔10秒启一次所以第二次加锁顺序变成231
好了关于main线程和timeoutChecker线程死锁的分析就先到这里了
现在回到项目中来由于我们的项目中有一个比较耗时的操作超时时间固定是60秒这个方法本来应该在Seata代理数据源之后做不知道为什么服务器上先执行了导致main线程等待了60秒之后才执行SeataDataSourceBeanPostProcessor#postProcessAfterInitialization() 最终解决方法时将PostConstruct注解去掉不在容器初始化的时候取做这么耗时的操作
如果采用Seata-1.5.2版本的话可能也不会出现死锁问题