Java程序会产生死锁?如何定位、修复?

news/2024/5/20 11:19:45 标签: java, 死锁, 基础

文章目录

    • 死锁的概念
    • 死锁定位
    • 实战
      • 模拟问题定位
        • 使用 jstack
        • 使用JConsole
        • 使用 Java 提供的标准管理 API

死锁的概念

  • 死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。

在这里插入图片描述

死锁定位

  • 定位死锁最常见的方式就是利用 jstack 等工具获取线程栈,然后定位互相之间的依赖关系,进而找到死锁。如果是比较明显的死锁,往往 jstack 等就能直接定位,类似 JConsole 甚至可以在图形界面进行有限的死锁检测
  • 如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身问题。所以,代码开发阶段互相审查,或者利用工具进行预防性排查,往往也是很重要的。

实战

一个基本的死锁程序例子

java">public class DeadLockSample extends Thread {
	private String first;
	private String second;
	public DeadLockSample(String name, String first, String second) {
    	super(name);
    	this.first = first;
    	this.second = second;
	}
 
	public  void run() {
    	synchronized (first) {
        	System.out.println(this.getName() + " obtained: " + first);
        	try {
            	Thread.sleep(1000L);
            	synchronized (second) {
                	System.out.println(this.getName() + " obtained: " + second);
            	}
        	} catch (InterruptedException e) {
            	// Do nothing
        	}
    	}
	}

	public static void main(String[] args) throws InterruptedException {
		// 死锁样例代码
    	String lockA = "lockA";
    	String lockB = "lockB";
    	DeadLockSample t1 = new DeadLockSample("Thread1", lockA, lockB);
    	DeadLockSample t2 = new DeadLockSample("Thread2", lockB, lockA);
    	t1.start();
    	t2.start();
    	t1.join();
    	t2.join();
	}

}
  • 程序编译执行后,几乎每次都可以重现死锁
    在这里插入图片描述

模拟问题定位

使用 jstack

  • 首先使用 jps 或者系统的 ps 命令、任务管理器等工具,确定进程 ID
java">jps

在这里插入图片描述

  • 然后,调用 jstack 获取线程栈(jstack在jdk包的bin目录下)
java">jstack 24468

在这里插入图片描述

"Thread2" #21 prio=5 os_prio=0 tid=0x000000001dfa3000 nid=0x3ab0 waiting for monitor entry [0x0000000020a6f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.carroll.test.thread.DeadLockSample.run(DeadLockSample.java:25)  //具体的类以及代码行数
        - waiting to lock <0x0000000776258c30> (a java.lang.String) // 等待获取锁  <0x0000000776258c30>
        - locked <0x0000000776258c68> (a java.lang.String)  // 已经持有锁  <0x0000000776258c68>

"Thread1" #20 prio=5 os_prio=0 tid=0x000000001dfa2000 nid=0x46f4 waiting for monitor entry [0x000000002096e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.carroll.test.thread.DeadLockSample.run(DeadLockSample.java:25)  //具体的类以及代码行数
        - waiting to lock <0x0000000776258c68> (a java.lang.String) // 等待获取锁  <0x0000000776258c68>
        - locked <0x0000000776258c30> (a java.lang.String) // 已经持有锁  <0x0000000776258c30>
  • 上面这个输出非常明显,找到处于 BLOCKED 状态的线程,按照试图获取(waiting)的锁 ID查找,很快就定位问题。 jstack 本身也会把类似的简单死锁抽取出来,直接打印出来。

实际应用中,类死锁情况未必有如此清晰的输出,但是总体上可以理解为:区分线程状态 -> 查看等待目标 -> 对比 Monitor 等持有状态

使用JConsole

  • 找到jdk包的bin目录,双击打开jconsole
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

使用 Java 提供的标准管理 API

  • ThreadMXBean:Java 虚拟机线程系统的管理接口。

    • Java 虚拟机具有此接口的实现类的单一实例。实现此接口的实例是一个 MXBean,可以通过调用ManagementFactory.getThreadMXBean() 方法或从平台 MBeanServer 方法获得它。
  • 在上面main方法中加入下面的代码片段:

java">ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
Runnable dlCheck = () -> {
	long[] threadIds = mbean.findDeadlockedThreads();
	if (threadIds != null) {
		ThreadInfo[] threadInfos = mbean.getThreadInfo(threadIds);
		System.out.println("Detected deadlock threads:");
		for (ThreadInfo threadInfo : threadInfos) {
			System.out.println(threadInfo.getThreadName());
		}
	}
};
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 稍等 5 秒,然后每 10 秒进行一次死锁扫描
scheduler.scheduleAtFixedRate(dlCheck, 5L, 10L, TimeUnit.SECONDS);

在这里插入图片描述

你知道的越多,你不知道的越多。


http://www.niftyadmin.cn/n/1007760.html

相关文章

《PyTorch深度学习实践》第八讲 加载数据集

b站刘二大人《PyTorch深度学习实践》课程第八讲加载数据集笔记与代码&#xff1a;https://www.bilibili.com/video/BV1Y7411d7Ys?p8&vd_sourceb17f113d28933824d753a0915d5e3a90 Dataset用于构造数据集&#xff0c;该数据集能够支持索引 DataLoader用于从数据集中拿出一个…

【论文精读】《Classifying User Activities in the Encrypted WeChat Traffic》

Classifying User Activities in the Encrypted WeChat Traffic Authors:Chengshang Hou,Junzheng Shi,Cuicui Kang,Zigang Cao,Xiong Gang Journal:2018 IEEE 37th International Performance Computing and Communications Conference (IPCCC) (2018) 摘要 加密移动应用程序…

springboot+echarts+mysql制作数据可视化大屏(滑动大屏)

作者水平低&#xff0c;如有错误&#xff0c;恳请指正&#xff01;谢谢&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 项目简单&#xff0c;适合大学生参考 分类专栏还有其它的可视化博客哦&#xff01; 专栏地址&#xff1a;https://blog.csdn.net/qq_559…

Spring FrameWork从入门到NB - 定制Bean

Customizing the Nature of a Bean&#xff0c;最早准备跳过这部分内容&#xff0c;但是觉得这部分内容是Spring Bean生命周期中的一个重要部分&#xff0c;跳过了可能会影响通往NB之路&#xff0c;所以还是要认真学习一下。 Spring通过三种类型的接口实现对Bean行为或状态的改…

C++ 程序设计入门

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

【探索AI未来】自动驾驶时代下的人工智能技术与挑战

自我介绍⛵ &#x1f4e3;我是秋说&#xff0c;研究人工智能、大数据等前沿技术&#xff0c;传递Java、Python等语言知识。 &#x1f649;主页链接&#xff1a;秋说的博客 &#x1f4c6; 学习专栏推荐&#xff1a;MySQL进阶之路、C刷题集、网络安全攻防姿势总结 欢迎点赞 &…

编程手记【2023-6-25-2023-6-30】

1、Call to ‘toArray()’ with pre-sized array argument ‘new Integer[list.size()]’ 问题&#xff1a; // 将List转为数组 List<Integer> list Arrays.asList(1,2,3,4,5,6); Integer[] array list.toArray(new Integer[list.size()]); System.out.println(Array…

Node.js中的process.nextTick与浏览器环境中的nextTick有何不同?

文章目录 nextTick 是一个用于异步操作的函数Node.js中的process.nextTick vs 浏览器环境中的nextTick1. 执行时机2. 微任务队列3. 堆栈溢出风险4. 兼容性 nextTick 是一个用于异步操作的函数 nextTick 是一个用于异步操作的函数&#xff0c;用来在当前执行栈执行完毕后&#…