一、死锁案例
Java多线程编程中,为了保证线程安全,用到了锁的概念,当两个线程互相等待对方释放同步的监视器时就会造成死锁。对于死锁,如果没有外力作用,死锁会一直继续下去,程序将无法正常往下执行。现在写一个简单的死锁案例。
先定义两个类,在这两个类中分别定义两个synchronized方法,这就表示如果有线程调用了对象中的方法,线程将持有该对象的锁。
ClassA.java
public class ClassA {
public synchronized void init(ClassB b) {
System.out.println("A对象的init方法执行了!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A对象准备调用B对象的end方法!");
b.end();
}
public synchronized void end() {
System.out.println("A对象的end方法执行了!");
}
}
ClassB.java
public class ClassB {
public synchronized void init(ClassA a) {
System.out.println("B对象的init方法执行了!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B对象准备调用A对象的end方法!");
a.end();
}
public synchronized void end() {
System.out.println("B对象的end方法执行了!");
}
}
然后再写一个测试类
TestMain.java
public class TestMain implements Runnable{
public ClassA a = new ClassA();
public ClassB b = new ClassB();
public static void main(String[] args) {
TestMain main = new TestMain();
Thread thread = new Thread(main);
thread.start();
main.runB();
}
@Override
public void run() {
a.init(b);
}
public void runB() {
b.init(a);
}
}
运行程序,执行结果如下:
B对象的init方法执行了!
A对象的init方法执行了!
B对象准备调用A对象的end方法!
A对象准备调用B对象的end方法!
程序在输出“A对象准备调用B对象的end方法!”之后就不再往下运行,但是从运行结果来看程序肯定是没有运行结束的,所以程序进入了一个假死的状态。
现在分析一下上面的代码
1、首先在main函数中定义一个子线程,然后调用线程的start()方法启动子线程,并且在main函数所在的线程(暂定叫做主线程)调用runB()方法,这样就有了两个线程在运行了。
2、在主线程的runB()方法中调用了ClassB对象的init方法,因为init方法有synchronized关键字,所以主线程获得ClassB对象的锁。程序首先输出B的init方法执行的内容,然后ClassB调用sleep方法执行休眠,休眠时不释放ClassB的对象锁,主线程让出CPU执行权,这时子线程获得CPU的执行权,子线程开始执行。
3、子线程调用ClassA的init方法,同样的获得了ClassA对象的锁并输出开始执行的内容,接着执行sleep方法开始休眠,子线程释放CPU执行权,但是不释放ClassA的对象锁。
4、这时主线程从休眠中醒来,获得CPU的执行权,准备执行ClassA的end方法,end方法也是被synchronized修饰的,所以主线程想获得ClassA的对象锁,但是因为ClassA的对象锁已被子线程拥有,所以主线程等待子线程释放锁。
5、接着子线程从休眠中醒来,准备执行ClassB的end方法,但是因为主线程拥有ClassB的对象锁,所以子线程也等待主线程释放锁,这就造成了相互等待的状态,程序不能继续往下执行。