很久以前学习Java的多线程机制时,一直没有搞懂Java的锁机制,今天花费了一整天的时间研究了一下。在学习的过程中发现一项非常有趣的问题,那就是死锁现象,下面是对死锁现象的完整描述:
当A线程等待由B线程持有的锁时,而B线程正在等待A线程持有的锁,随即发生死锁现象,
JVM不会检测也不试图避免这种情况,完全需要靠程序员自己注意。
要避免死锁现象,我们首先需要搞清楚什么是死锁现象,然后才能找到有效的避免方法,接下来我们将探索什么事死锁现象。
一、死锁现象
阅读上面对死锁现象的定义,我们可以断定,死锁现象至少需要两个线程,这里我们假设为Thread A和Thread B。还有,我们知道,每个对象的同步锁同一时刻只能被一个线程持有,所以,根据上面定义,我们还可以断定,这里必须包含两个带锁的对象,我们将这两个带锁的对象命名为Lock X和Lock Y。下面我们来创建代码,观察死锁现象的具体表现形式:
1) 首先创建两个带锁的对象类
// Lock X
static class LockX {
synchronized public void get(LockY lock) {
System.out.println("Before sleep of lock X");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Waiting for lock Y");
lock.samething();
}
synchronized public void something() {
System.out.println("Lock X somthing method");
}
}
static class LockY {
synchronized public void get(LockX lock) {
System.out.println("Before sleep of lock Y");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Waiting for lock X");
lock.something();
}
synchronized public void samething() {
System.out.println("Lock Y somthing method");
}
}
上面代码看起来可能很奇怪,难以理解。现在先不要着急去理解每个方法的用意。最后我们会详细讲解每个方法的具体调用过程。
2) 创建两个线程类
// Thread A
static class ThreadA extends Thread {
private LockX lockX;
private LockY lockY;
public ThreadA(LockX lockx, LockY locky) {
this.lockX = lockx;
this.lockY = locky;
}
@Override
public void run() {
System.out.println("Start run thread A");
lockX.get(lockY);
}
}
// Thread B
static class ThreadB extends Thread {
private LockY lockY;
private LockX lockX;
public ThreadB(LockX lockx, LockY locky) {
this.lockX = lockx;
this.lockY = locky;
}
@Override
public void run() {
System.out.println("Start run thread B");
lockY.get(lockX);
}
}
3)创建主方法
public static void main(String[] args) {
LockX x = new LockX();
LockY y = new LockY();
new ThreadA(x, y).start();
new ThreadB(x, y).start();
}
首先运行一次程序,观察是否真的发生死锁现象。下面截图展示了控制台的运行结果: