最近在研究Java并发,学习死锁时偶然发现了一种嵌套管程锁死,所以自己实现了下,可能在不小心中就会犯这种错误。
1、死锁实现
死锁原理很简单,就是线程1先获取锁A,在获取锁B;而线程2先获取锁B,在获取锁A,由于两个线程获取顺序不一样,都没有将各自的锁释放,所以就出现了死锁。代码实现也很简单:
public class DeathLock implements Runnable{
private boolean flag = true;
private Object o1 = new Object();
private Object o2 = new Object();
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
if (flag) {
System.out.println(Thread.currentThread().getName()+ " 输入为:" + flag);
flag = false;
synchronized (o1) {
try {
Thread.sleep(2000);
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+ " 我是真的");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
System.out.println(Thread.currentThread().getName()+ " 输入为:" + flag);
synchronized (o2) {
try {
Thread.sleep(1000);
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+ " 我是假的");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
DeathLock lock1 = new DeathLock();
Thread th1 = new Thread(lock1);
Thread th2 = new Thread(lock1);
th1.start();
th2.start();
}
}
简单运行下,
Thread-0 输入为:true
Thread-1 输入为:false
然后都停留在获取第二个锁的阶段,从而照成死锁,解决死锁也很简单,要么保持获取锁的顺序一致,要么就是保证获取锁时,没有其他线程占有锁,或者用JDK1.5后提供的Lock实现都可以,比如tryLock等方法,获取之前先判断下。
2、嵌套管程锁死
前几天再并发编程网上发现,然后自己实现了一下,这种情况不容易制造,也比较难于重现。其照成的原因是:线程1获取了A和B锁,然后释放B锁,等待线程2发过来的信号,然后释放A锁;线程2必须同时获取A锁和B锁,才能向线程1发送信号,这样就会照成锁死。代码实现也不复杂,就在上述代码中添加一个lock和unlock方法,run方法进行修改下:
public void lock() {
synchronized (o1) {
while (!flag) {
try {
Thread.sleep(200);
synchronized (o2) {
o2.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = false;
}
}
public void unlock() {
synchronized (o1) {
try {
Thread.sleep(200);
flag = true;
synchronized (o2) {
o2.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void run(){
lock();
try {
System.out.println(Thread.currentThread().getName() + " : 我获得了锁");
} finally {
System.out.println(Thread.currentThread().getName() + " : 我将要释放锁");
unlock();
System.out.println(Thread.currentThread().getName() + " : 我释放了锁");
}
}
这是一段简单的lock实现,原理类似java新增concurrent包的Lock类
执行结果为:
Thread-0 : 我获得了锁
Thread-0 : 我将要释放锁
由此可以看出,线程Thread-0 在执行unlock时就锁死了,因为lock的o1锁没有进行释放,而unlock又需要获取o1锁,那么就照成死锁了。
当然,由于测试时都是用的同一个实例,没有释放当然或照成锁死了。确实,如果是对两个不同的实例进行测试,就不会出现这种情形,当然也就不会有线程安全问题了。
总之,不管什么形式的死锁,造成的原因就是就是因为同一个锁没有被正确的释放时,其他线程希望获取改锁,当然,在JDK1.5之后,提供的Lock类就提供了避免这种情况的方式,使用起来非常方便。