isr*_*kir 62 java multithreading deadlock
很久以前,我从Java参考书中保存了一句话:"Java没有处理死锁的机制.它甚至不知道发生了死锁." (Head First Java 2nd Edition,p.516)
那么,它是什么呢?有没有办法在Java中捕获死锁案例?我的意思是,有没有一种方法可以让我们的代码了解发生死锁的情况?
sta*_*fan 79
从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);
}
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*ler 11
JDK 5和6将把持有的锁信息转储到完整的线程转储中(使用kill -3,jstack,jconsole等获得).JDK 6甚至包含有关ReentrantLock和ReentrantReadWriteLock的信息.从这些信息中可以通过找到锁定周期来诊断死锁:线程A保持锁定1,线程B保持锁定2,并且A正在请求2或B正在请求1.根据我的经验,这通常是非常明显的.
其他分析工具实际上可以找到潜在的死锁,即使它们没有发生.来自OptimizeIt,JProbe,Coverity等供应商的线程工具是很好看的地方.
请注意,使用并发包非常难以调试的死锁类型.这就是你有一个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!");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
通常,java不提供死锁检测.与使用显式锁定的语言相比,synchronized关键字和内置监视器使得解决死锁更加困难.
我建议迁移到使用java.util.concurrent.Lock锁等,以使您的锁定方案更容易推理.实际上,您可以轻松地使用死锁检测自行实现锁定接口.该算法基本上遍历锁依赖图并寻找一个循环.
Java可以检测死锁(虽然不是在运行时,但它仍然可以诊断并报告它)。
例如,当使用下面的“Saurabh M. Chande”代码的稍微修改版本时(将其更改为 Java 并添加一些计时以保证每次运行的锁定)。一旦你运行它并且它死锁,如果你输入:
kill -3 PID # where 'PID' is the Linux process ID
Run Code Online (Sandbox Code Playgroud)
它将生成一个堆栈转储,其中包含以下信息:
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"
Run Code Online (Sandbox Code Playgroud)
如果您遵循一个简单的规则,就可以避免死锁:让所有线程以相同的顺序声明和释放它们的锁。通过这种方式,您永远不会陷入可能发生死锁的情况。
甚至哲学家进餐问题也可以被视为违反此规则,因为它使用了左右勺子的相对概念,导致不同的线程使用不同的勺子分配顺序。如果勺子的编号是唯一的,而哲学家们都试图先得到编号最低的勺子,那么就不可能出现僵局。
在我看来,预防胜于治疗。
这是我喜欢遵循的两个准则之一,以确保线程正常工作。另一个是确保每个线程单独负责自己的执行,因为它是唯一一个在任何时间点都完全了解自己在做什么的线程。
所以这意味着没有Thread.stop
调用,使用全局标志(或消息队列或类似的东西)告诉另一个线程你想要采取行动。然后让该线程完成实际工作。
归档时间: |
|
查看次数: |
40437 次 |
最近记录: |