一次死锁的排查
目录
问题
解决方法
System.exit(1)
Runtime.getRuntime().halt(1)
问题
动作: 变更了服务器的ip,然后进行更换IP 的动作;服务器重启后,再也启动不来了。
发现进行还在,根据进程号 netstat -ano|findstr pid 可以看到一些6643等服务还在。8970可能不在了。
jconsole pid 发现了其中有死锁。
观察日志 error.log发现 地址变更后,mqtt 已经在做connectlost里处理进行重连了!但这个时候它不断占用锁;但spring-boot销毁bean的程序已经也要占用锁。所以死锁了
但ip又不能写127,因为前端会使用这个ip.
Found one Java-level deadlock:
=============================
"Thread-34":waiting to lock monitor 0x000000001faa28e8 (object 0x00000000854258c0, a org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter),which is held by "MQTT Con: 123456-inbound"
"MQTT Con: 123456-inbound":waiting for ownable synchronizer 0x0000000085425c58, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),which is held by "Thread-34"Java stack information for the threads listed above:
===================================================
"Thread-34":at org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter.doStop(MqttPahoMessageDrivenChannelAdapter.java:178)- waiting to lock <0x00000000854258c0> (a org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter)at org.springframework.integration.endpoint.AbstractEndpoint.stop(AbstractEndpoint.java:173)at com.gbcom.wvp.util.ServerIPCheck$1.run(ServerIPCheck.java:135)at java.lang.Thread.run(Thread.java:748)
"MQTT Con: 123456-inbound":at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x0000000085425c58> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)at org.springframework.integration.endpoint.AbstractEndpoint.isRunning(AbstractEndpoint.java:142)at org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter.connectionLost(MqttPahoMessageDrivenChannelAdapter.java:346)- locked <0x00000000854258c0> (a org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter)at org.eclipse.paho.client.mqttv3.internal.CommsCallback.connectionLost(CommsCallback.java:304)at org.eclipse.paho.client.mqttv3.internal.ClientComms.shutdownConnection(ClientComms.java:439)at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:740)at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.
解决方法
直接把原来比较优雅的销毁,变成粗暴的销毁,还可以加快速度 。
我将原来的
System.exit(1)
替换为Runtime.getRuntime().halt(1)
,这两种方法的区别在于:System.exit(1)
- 触发正常的JVM关闭序列
- 调用所有已注册的关闭钩子(包括Spring的上下文关闭过程)
- 等待所有Bean的销毁方法执行完毕
- 这可能导致死锁,正如你在MQTT适配器中遇到的问题
Runtime.getRuntime().halt(1)
- 强制终止JVM,不执行任何清理操作
- 立即停止所有线程(除了调用halt的线程)
- 不触发关闭钩子
- 不等待Bean销毁或其他清理操作
- 直接终止程序,操作系统回收所有资源