java代码死锁伪代码 java死锁的解决方法( 四 )


◆ ThreadA和ThreadB都被阻塞,程序死锁 。
当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出 , 对于规模较大的多线程程序来说尤其如此 。好的线程分析工具,例如JProbe Threadalyzer能够分析死锁并指出产生问题的代码位置 。
隐性死锁
隐性死锁由于不规范的编程方式引起 , 但不一定每次测试运行时都会出现程序死锁的情形 。由于这个原因 , 一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大 。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待 。
加锁次序
当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形 。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁 。考虑下面的情形 , ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下:
◆ ThreadA获得lock_1;
◆ ThreadA被抢占,VM调度程序转到ThreadB;
◆ ThreadB获得lock_2;
◆ ThreadB被抢占,VM调度程序转到ThreadA;
◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;
◆ 调度程序转到ThreadB;
◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;
◆ ThreadA和ThreadB死锁 。
必须指出的是,在代码丝毫不做变动的情况下 , 有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断 。在这种情形下 , 常规的死锁检测很难确定错误所在 。
占有并等待
如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二 。
//代码二
public class queue {
static java.lang.Object queueLock_;
Producer producer_;
Consumer consumer_;
public class Producer {
void produce() {
while (!done) {
“synchronized” (queueLock_) {
produceItemAndAddItToQueue();
“synchronized” (consumer_) {
consumer_.notify();
}
}
}
}
public class Consumer {
consume() {
while (!done) {
“synchronized” (queueLock_) {
“synchronized” (consumer_) {
consumer_.wait();
}
removeItemFromQueueAndProcessIt();
}
}
}
}
}
}
在代码二中,Producer向队列加入一项新的内容后通知Consumer , 以便它处理新的内容 。问题在于,Consumer可能保持加在队列上的锁 , 阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁 。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁 。
在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁 。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的 。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等 。
要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可 。
java代码死锁伪代码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java死锁的解决方法、java代码死锁伪代码的信息别忘了在本站进行查找喔 。

推荐阅读