共计 3131 个字符,预计需要花费 8 分钟才能阅读完成。
作者:Yujiaao
https://segmentfault.com/a/11…
经典但外围 Java 面试问题之一。
如果你没有参加过多线程并发 Java 应用程序的编码,你可能会失败。
如何防止 Java 线程死锁?
如何防止 Java 中的死锁?
这是 Java 面试的热门问题之一, 也是多线程的编程中的重口味之一, 次要在招高级程序员时容易被问到, 且有很多后续问题。
只管问题看起来十分根本, 但大多数 Java 开发人员一旦你开始深刻, 就会陷入困境。
面试问题总是以“什么是死锁?????”开始
当两个或多个线程在期待彼此开释所需的资源 (锁定) 并陷入有限期待即是死锁。它仅在多任务或多线程的状况下产生。
如何检测 Java 中的死锁?
尽管这能够有很多答案, 但我的版本是首先我会看看代码, 如果我看到一个嵌套的同步块,或从一个同步的办法调用其余同步办法, 或试图在不同的对象上获取锁, 如果开发人员不是十分小心,就很容易造成死锁。
另一种办法是在运行应用程序时理论锁定时找到它, 尝试采取线程转储, 在 Linux 中, 你能够通过 kill -3
命令执行此操作, 这将打印应用程序日志文件中所有线程的状态, 并且你能够看到哪个线程被锁定在哪个线程对象上。
你能够应用 fastthread.io 网站等工具剖析该线程转储, 这些工具容许你上载线程转储并对其进行剖析。
另一种办法是应用 jConsole 或 VisualVM, 它将显示哪些线程被锁定以及哪些对象被锁定。
如果你有趣味理解故障排除工具和剖析线程转储的过程, 我倡议你看看 Uriah Levy 在多元视觉 (PluraIsight) 上《剖析 Java 线程转储》课程。旨在具体理解 Java 线程转储, 并相熟其余风行的高级故障排除工具。
编写一个将导致死锁的 Java 程序?
一旦你答复了后面的问题, 他们可能会要求你编写代码, 这将导致 Java 死锁。
这是我的版本之一
/**
* Java 程序通过强制循环期待来创立死锁。*/
public class DeadLockDemo {
/*
* 此办法申请两个锁, 第一个字符串, 而后整数
*/
public void method1() {synchronized (String.class) {System.out.println("Aquired lock on String.class object");
synchronized (Integer.class) {System.out.println("Aquired lock on Integer.class object");
}
}
}
/*
* 此办法也申请雷同的两个锁, 但齐全
* 相同的程序, 即首先整数, 而后字符串。* 如果一个线程持有字符串锁, 则这会产生潜在的死锁
* 和其余持有整数锁, 他们期待对方, 永远。*/
public void method2() {synchronized (Integer.class) {System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {System.out.println("Aquired lock on String.class object");
}
}
}
}
如果 method1() 和 method2() 都由两个或多个线程调用, 则存在死锁的可能性, 因为如果线程 1 在执行 method1() 时在 Sting 对象上获取锁, 线程 2 在执行 method2() 时在 Integer 对象上获取锁, 期待彼此开释 Integer 和 String 上的锁以持续进行一步, 但这永远不会产生。
此图准确演示了咱们的程序, 其中一个线程在一个对象上持有锁, 并期待其余线程持有的其余对象锁。
你能够看到, Thread1 须要 Thread2 持有的 Object2 上的锁, 而 Thread2 心愿取得 Thread1 持有的 Object1 上的锁。因为没有线程违心放弃, 因而存在死锁, Java 程序被卡住。
其理念是, 你应该晓得应用常见并发模式的正确办法, 如果你不相熟这些模式, 那么 Jose Paumard《利用于并发和多线程的常见 Java 模式》是学习的好终点,另外关注微信公众号 Java 技术栈,也能够浏览很多设计模式干货。
如何防止 Java 中的死锁?
当初面试官来到最初一部分, 在我看来, 最重要的局部之一; 如何修复代码中的死锁?或如何防止 Java 中的死锁?
如果你认真查看了下面的代码, 那么你可能曾经发现死锁的真正起因不是多个线程, 而是它们申请锁的形式, 如果你提供有序拜访, 则问题将失去解决。
上面是我的修复版本, 它通过防止循环期待,而防止死锁, 而不须要抢占, 这是须要死锁的四个条件之一。
public class DeadLockFixed {
/**
* 两种办法当初都以雷同的程序申请锁, 首先采纳整数, 而后是 String。* 你也能够做反向, 例如, 第一个字符串, 而后整数,
* 只有两种办法都申请锁定, 两者都能解决问题
* 程序统一。*/
public void method1() {synchronized (Integer.class) {System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {System.out.println("Aquired lock on String.class object");
}
}
}
public void method2() {synchronized (Integer.class) {System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {System.out.println("Aquired lock on String.class object");
}
}
}
}
当初没有任何死锁,因为两种办法都按雷同的程序拜访 Integer 和 String 类文本上的锁。
因而, 如果线程 A 在 Integer 对象上获取锁, 则线程 B 不会持续, 直到线程 A 开释 Integer 锁, 即便线程 B 持有 String 锁, 线程 A 也不会被阻止, 因为当初线程 B 不会冀望线程 A 开释 Integer 锁以持续。
近期热文举荐:
1.Java 15 正式公布,14 个新个性,刷新你的认知!!
2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!
3. 我用 Java 8 写了一段逻辑,共事直呼看不懂,你试试看。。
4. 吊打 Tomcat,Undertow 性能很炸!!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!