Java的死锁分析
在编写并发程序的时候,死锁问题不得不面对,注意,消除。
何为死锁
wiki的解释是这样的:“这里指的是进程 死锁,是一个计算机技术的名词。它是操作系统 或软件运行的一种状态:在多工系统下,当一个或多个进程等待系统资源,而系统资源又同时被此进程本身或者其 它进程占用,就形成了死锁。”
例如,线程1锁住了资源A,并试图去访问资源B,而此时线程2锁住了资源B,并试图访问资源A,这样线程1和2均锁住了对方需要的资源,并且都在等 待对方持有的资源释放后才能继续运行释放自己持有的资源,死锁发生了,两个线程都因为等待资源被阻塞,假如没有一种手段来解除这种互相等待,可以想象,在 理论上二者都会永远等待下去。
这种现象可以这样说明:
Thread 1 locks A,waits for B
Thread 2 locks B,wats for A
下面我们展示一段java代码:
public class TreeNode {
TreeNode parent = null;
List children = new ArrayList();
public synchronized void addChild(TreeNode child) {
if (!this.children.contains(child)) {
this.children.add(child);
child.setParentOnly(this);
}
}
public synchronized void addChildOnly(TreeNode child) {
if (!this.children.contains(child)) {
this.children.add(child);
}
}
public synchronized void setParent(TreeNode parent) {
this.parent = parent;
parent.addChildOnly(this);
}
public synchronized void setParentOnly(TreeNode parent) {
this.parent = parent;
}
}
假如有两个TreeNode实例:child,parent。如果线程(1)调用parent.addChild(child),同时,线程(2) 调用child.setParent(parent)。对线程(1)来说,他锁住了parent对象,而线程(2)同时锁住了child对象。在这个时 候,线程(1)试图去请求child,要获得child对象的锁,但是由于这把锁已经被线程(2)持有,线程(1)不得不阻塞等待,当线程(2)执行到 parent.addChildOnly(this)时,他要获得parent的锁,很不幸的是,这把锁已经被parent占据了,他也不得不阻塞等待。 就这样,杯具发生了,线程(1)和线程(2)都将阻塞等待下去。
更复杂的死锁现象
Thead 1 locks A,waits for B,
Thead 2 locks B,waits for C,
Thead 3 locks C,waits for D,
Thead 4 locks D,waits for A
数据库的死锁
一个事务已经锁定了一条记录(a)进行修改,现在,他需要修改记录(b),当他去请求记录(b)的锁时,很不幸,记录(b)被另外一个事务锁住,更碰巧的是,持有记录(b)的锁的事务已经在这个时候刚好要去请求记录(a)的锁,死锁再次发生。下面的现象可以说明:
Transaction 1 request 1,locks record 1 for update
Transaction 2 request 1,locks record 2 for update
Transaction 1 request 2,locks record 2 for update
Transaction 2 request 2,locks record 1 for update