java代码实现一个死锁 java简单实现死锁( 二 )


隐性死锁
隐性死锁由于不规范的编程方式引起 , 但不一定每次测试运行时都会出现程序死锁的情形 。由于这个原因 , 一些隐性死锁可能要到应用正式发布之后才会被发现 , 因此它的危害性比普通死锁更大 。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待 。
加锁次序
当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形 。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁 。考虑下面的情形,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程序中处理数据库超时与死锁?每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况,而这些情况需要在代码中编程来解决;本文主要介绍与数据库事务死锁等情况相关的重试逻辑概念 , 此外,还会探讨如何避免死锁等问题 , 文章以DB2(版本9)与为例进行讲解 。
什么是数据库锁定与死锁锁定(Locking)发生在当一个事务获得对某一资源的“锁”时 , 这时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性;在设计与数据库交互的程序时,必须处理锁与资源不可用的情况 。

推荐阅读