简单来讲,死锁就是两个线程各自获得一个监视器的情况下,在互相等待对方的监视器,就会发生死锁。
public class DeadLock { private final Object obj1 = new Object(); private final Object obj2 = new Object(); public void leftRight() throws Exception { synchronized (obj1) { Thread.sleep(500); synchronized (obj2) { System.out.println("I'm in leftRight!"); } } } public void rightLeft() throws Exception { synchronized (obj2) { Thread.sleep(500); synchronized (obj1) { System.out.println("I'm in rightLeft!"); } } } public static void main(String[] args) throws InterruptedException { DeadLock deadLock = new DeadLock(); Thread0 thread0 = new Thread0(deadLock); Thread1 thread1 = new Thread1(deadLock); thread0.start(); thread1.start(); } } class Thread0 extends Thread { private DeadLock dLock; public Thread0(DeadLock dLock) { this.dLock = dLock; } public void run() { try { dLock.leftRight(); } catch (Exception e) { e.printStackTrace(); } } } class Thread1 extends Thread { private DeadLock dLock; public Thread1(DeadLock dLock) { this.dLock = dLock; } public void run() { try { dLock.rightLeft(); } catch (Exception e) { e.printStackTrace(); } } }
运行该程序会发现,什么结果都不会打印,因为程序死锁了。那么如何定位死锁的问题呢?
1、使用jps获得当年java虚拟机进程的pid
2、使用jstack工具打印堆栈,在打印信息的最后会告诉我们发现了一处死锁。(自己也可以根据打印信息分析分析)
可以看到,thread-1锁住了e1cda4b0,然后想获得e1cda4a0这个监视器,然后同时thread-0锁住了e1cda4a0,想获得e1cda4b0,于是就造成了deadlock。
那么我们该如何预防死锁呢?
首先我们要弄明白死锁产生的条件是什么?
死锁产生条件4个:互斥条件、不可抢占条件、占有并等待、循环等待条件。
死锁预防:死锁4个条件其中一个不满足就不会发生,一般从后三个入手。
破坏不可抢占条件:当已占有资源的线程,又要申请新的资源,但不能立即被满足时,必须释放所占有的全部资源,以后再重新申请。
破坏占有并等待:实行资源预先分配策略,一次性分配资源给他,若不满足则不分配
破坏循环等待:实行资源有序分配策略,所以线程按规定顺序申请资源,这样就不会形成环路。
死锁避免:允许前三个条件存在,通过合理的资源分配算法来确保不会形成环形等待,来避免四个条件都成立从而避免死锁。有银行家算法。