当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
class A {
public synchronized void foo(B b) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入 A 实例的 foo() 方法");
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("当前线程名:" + Thread.currentThread().getName() + " 企图调用 B 实例的 last() 方法");
b.last();
}
public synchronized void last() {
System.out.println("进入了 A 类的 last() 方法内部");
}
}
class B {
public synchronized void bar(A a) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入了B实例的bar()方法");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名 " + Thread.currentThread().getName() + " 企图调用A实例的 last() 方法");
a.last();
}
public synchronized void last() {
System.out.println("进入了 B 类的 last() 方法内部");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
Thread.currentThread().setName("主线程");
a.foo(b);
System.out.println("进入了主线程之后");
}
@Override
public void run() {
Thread.currentThread().setName("副线程");
b.bar(a);
System.out.println("进入了副线程之后");
}
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(dl).start();
dl.init();
}
}
避免死锁的常见方式:
避免多次锁定:尽量避免同一个线程对多个同步监视器进行锁定。比如上面的死锁程序,主线程要对A、B两个对象(同步监视器)进行锁定,副线程也要对A、B两个对象进行锁定,这就埋下了导致死锁的隐患。
具有相同的加锁顺序:如果多个线程需要对多个同步监视器进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对A对象(同步监视器)加锁,再对B对象(同步监视器)加锁;而副线程则先对B对象加锁,再对A对象加锁。这种方式很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按相同的顺序加锁,就可以避免死锁问题。
使用定时锁:程序调用Lock对象的tryLock()方法加锁时可指定time和unit参数,当超过指定时间后会自动释放对Lock的锁定,这样就可以解开死锁了