很久以前,我从Java参考书中保存了一句话:“Java没有处理死锁的机制。它甚至不知道发生了死锁。” (Head First Java 2nd Edition,p.516)
那么,它是什么?有没有办法在Java中捕获死锁案例?我的意思是,有没有办法让我们的代码理解发生了死锁的情况?
答案 0 :(得分:78)
从JDK 1.5开始,java.lang.management
包中有非常有用的方法来查找和检查发生的死锁。请参阅findMonitorDeadlockedThreads()
类的findDeadlockedThreads()
和ThreadMXBean
方法。
使用此方法的一种可能方法是使用单独的监视程序线程(或定期任务)执行此操作。
示例代码:
ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
long[] ids = tmx.findDeadlockedThreads();
if (ids != null) {
ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
System.out.println("The following threads are deadlocked:");
for (ThreadInfo ti : infos) {
System.out.println(ti);
}
}
答案 1 :(得分:18)
JConsole能够检测正在运行的应用程序中的死锁。
答案 2 :(得分:11)
JDK 5和6将把保存的锁信息转储到完整的线程转储中(使用kill -3,jstack,jconsole等获得)。 JDK 6甚至包含有关ReentrantLock和ReentrantReadWriteLock的信息。从这些信息中可以通过找到锁定周期来诊断死锁:线程A保持锁定1,线程B保持锁定2,并且A正在请求2或B正在请求1.根据我的经验,这通常是非常明显的。
其他分析工具实际上可以找到潜在的死锁,即使它们没有发生。来自OptimizeIt,JProbe,Coverity等供应商的线程工具是很好看的地方。
答案 3 :(得分:8)
请注意,使用并发包非常难以调试的死锁类型。这就是你有一个ReentrantReadWriteLock并且一个线程抓住读锁定然后(比方说)试图进入一个其他线程持有的监视器,该线程也在等待抓取写锁定。使调试特别困难的原因是没有人输入读锁的记录。这只是一个计数。该线程甚至可能抛出异常并死亡,使读取计数不为零。
以下是前面提到的findDeadlockedThreads方法无法获取的示例死锁:
import java.util.concurrent.locks.*;
import java.lang.management.*;
public class LockTest {
static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) throws Exception {
Reader reader = new Reader();
Writer writer = new Writer();
sleep(10);
System.out.println("finding deadlocked threads");
ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
long[] ids = tmx.findDeadlockedThreads();
if (ids != null) {
ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
System.out.println("the following threads are deadlocked:");
for (ThreadInfo ti : infos) {
System.out.println(ti);
}
}
System.out.println("finished finding deadlocked threads");
}
static void sleep(int seconds) {
try {
Thread.currentThread().sleep(seconds*1000);
} catch (InterruptedException e) {}
}
static class Reader implements Runnable {
Reader() {
new Thread(this).start();
}
public void run() {
sleep(2);
System.out.println("reader thread getting lock");
lock.readLock().lock();
System.out.println("reader thread got lock");
synchronized (lock) {
System.out.println("reader thread inside monitor!");
lock.readLock().unlock();
}
}
}
static class Writer implements Runnable {
Writer() {
new Thread(this).start();
}
public void run() {
synchronized (lock) {
sleep(4);
System.out.println("writer thread getting lock");
lock.writeLock().lock();
System.out.println("writer thread got lock!");
}
}
}
}
答案 4 :(得分:6)
通常,java不提供死锁检测。 synchronized锁定和内置监视器使得解决死锁的难度比使用显式锁定的语言更难。
我建议迁移到使用java.util.concurrent.Lock锁等,以使您的锁定方案更容易推理。实际上,您可以轻松地使用死锁检测自行实现锁定接口。该算法基本上遍历锁依赖图并寻找一个循环。
答案 5 :(得分:4)
如果遵循一个简单的规则,就可以避免死锁:让所有线程声明并以相同的顺序释放它们的锁。通过这种方式,您永远不会遇到可能发生死锁的情况。
即使是餐饮哲学家的问题也可以被视为违反这一规则,因为它使用左右勺子的相对概念,这导致不同的线程使用不同的勺子分配顺序。如果勺子的编号是唯一的,并且哲学家们都试图先得到编号最低的勺子,那么僵局就不可能了。
在我看来,预防胜于治疗。
这是我希望遵循的两个准则之一,以确保线程正常工作。另一个是确保每个线程完全负责自己的执行,因为它是唯一一个完全了解它在任何时间点正在做什么的。
这意味着没有Thread.stop
调用,使用全局标志(或消息队列或类似的东西)来告诉您想要采取行动的另一个线程。然后让该线程完成实际工作。
答案 6 :(得分:4)
如果您使用的是Java 5,则可以调用ThreadMXBean上的方法findMonitorDeadlockedThreads()
,您可以通过调用java.lang.management.ManagementFactory.getThreadMXBean()
来调用该方法。这将只发现由对象监视器引起的死锁。在Java 6上有findDeadlockedThreads()
,它还会发现由“可拥有的同步器”(例如ReentrandLock
和ReentrantReadWriteLock
)引起的死锁。
请注意,调用这些方法可能会很昂贵,因此它们只能用于故障排除。
答案 7 :(得分:3)
不完全是您提出的问题,但是当发生死锁时,您可以对进程ID执行“kill -3”,并将线程转储转储到stdout。此外,1.6 jvm还有一些工具可以用gui方式做同样的事情。
答案 8 :(得分:3)
如果从命令行运行并且怀疑自己已经死锁,请尝试在Windows中按ctrl + break(在unix中使用ctrl + \)来获取线程转储。 见http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html
答案 9 :(得分:3)
博士。 JavaSpecialists的Heinz Kabutz编写了一个有趣且信息丰富的newsletter issue on Java deadlocks,并在another newsletter issue中描述了一个名为ThreadMXBean的东西。在这些之间,你应该很好地了解问题和一些指导你做自己的仪器。
答案 10 :(得分:3)
Java可以检测死锁(虽然不是在运行时,它仍然可以诊断并报告死锁)。
例如,当使用稍微修改过的'Saurabh M. Chande'代码时(将其更改为Java并添加一些时间来保证每次运行时锁定)。一旦你运行它并且它就会死锁,如果你输入:
kill -3 PID # where 'PID' is the Linux process ID
它将生成堆栈转储,其中包含以下信息:
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x08081670 (object 0x7f61ddb8, a Deadlock$A),
which is held by "main"
"main":
waiting to lock monitor 0x080809f0 (object 0x7f61f3b0, a Deadlock$B),
which is held by "Thread-0"
答案 11 :(得分:2)
如果你在eclipse中调试,你可以暂停应用程序(在调试视图中选择应用程序和调试工具栏上的小||按钮)然后它可以报告死锁。
有关示例,请参阅http://runnerwhocodes.blogspot.com/2007/10/deadlock-detection-with-eclipse.html。
答案 12 :(得分:1)
从Java 1.8开始,您可以通过在终端上使用jcmd
命令轻松地找到程序是否有死锁。
jcmd
:它列出了所有使用Java的程序(PID和名称)。jcmd <PID> Thread.print
:这将在控制台上打印线程转储,并且在程序出现死锁时也将打印。您可以使用命令jcmd
列出的更多jcmd <PID> help
选项来分析Java程序
答案 13 :(得分:0)
Java 5引入了ThreadMXBean - 一个为线程提供各种监视方法的接口。 ...区别在于findDeadlockedThreads还可以检测由所有者锁(java.util.concurrent)引起的死锁,而findMonitorDeadlockedThreads只能检测监视器锁(即同步块)
或者您可以以编程方式检测它,请参阅此https://dzone.com/articles/how-detect-java-deadlocks
答案 14 :(得分:-1)
经过这么长时间,我能够编写最简单的死锁示例。 欢迎评论。
Class A
{
synchronized void methodA(B b)
{
b.last();
}
synchronized void last()
{
SOP(“ Inside A.last()”);
}
}
Class B
{
synchronized void methodB(A a)
{
a.last();
}
synchronized void last()
{
SOP(“ Inside B.last()”);
}
}
Class Deadlock implements Runnable
{
A a = new A();
B b = new B();
// Constructor
Deadlock()
{
Thread t = new Thread();
t.start();
a.methodA(b);
}
public void run()
{
b.methodB(a);
}
public static void main(String args[] )
{
new Deadlock();
}
}
答案 15 :(得分:-4)
你必须在死锁类
中稍微修改一下代码Deadlock() { Therad t = new Thread(this); // modified t.start(); System.out.println(); //any instruction to delay a.methodA(b); }
此外,上述代码不会总是导致死锁,只会发生一些时间。