定义:死锁是指两个或者两个以上的线程在执行过程中,因争夺资源而造成的相互等待的现象。
死锁产生的四个条件:
互斥条件:指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。
持有并请求条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源。
不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。
环路等待条件:指在发生死锁时,必然存在一个线程—资源的环形链,即线程集合{T0, T1, T2, …, Tn}中的T0正在等待一个T1占用的资源,T1正在等待T2占用的资源,……Tn正在等待已被T0占用的资源。
死锁例子:
public class DeadLock { private static Object resourceA = new Object(); private static Object resourceB = new Object(); public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceA!"); try { Thread.sleep(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceB!"); synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceB!"); } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceB!"); try { Thread.sleep(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceA!"); synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceA!"); } } } }); threadA.start(); threadB.start(); } }
输出:
分析:本例是如何满足死锁的四个条件的。
首先,resourceA和resourceB都是互斥资源,当线程A调用synchronized(resourceA)方法获取到resourceA上的监视器锁并释放前,线程B再调用synchronized(resourceA)方法尝试获取该资源会被阻塞,只有线程A主动释放该锁,线程B才能获得,这满足了资源互斥条件。
线程A首先通过synchronized(resourceA)方法获取到resourceA上的监视器锁资源,然后通过synchronized(resourceB)方法等待获取resourceB上的监视器锁资源,这就构成了持有并请求条件。
线程A在获取resourceA上的监视器锁资源后,该资源不会被线程B掠夺走,只有线程A自己主动释放resourceA资源时,它才会放弃对该资源的持有权,这构成了资源的不可剥夺条件。
线程A持有objectA资源并等待获取objectB资源,而线程B持有objectB资源并等待objectA资源,这构成了环路等待条件。所以线程A和线程B就进入了死锁状态。
避免线程
避免死锁,只需要破坏掉至少一个构造死锁的必要条件即可,但是,目前只有持有并请求和环路等待条件是可以被破坏的。
1.破坏环路等待条件
public class DeadLock{ private static Object resourceA = new Object(); private static Object resourceB = new Object(); public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceA!"); try { Thread.sleep(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceB!"); synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceB!"); } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceB!"); try { Thread.sleep(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceA!"); synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceA!"); } } } }); threadA.start(); threadB.start(); } }
输出:
2.破坏持有并请求条件
public class DeadLockTest2 { private static Object resourceA = new Object(); private static Object resourceB = new Object(); public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceA!"); try { resourceA.wait(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceB!"); synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceB!"); } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceB){ System.out.println(Thread.currentThread() + "get resourceB!"); try { resourceB.wait(1000); }catch (InterruptedException exception){ exception.printStackTrace();; } System.out.println(Thread.currentThread() + "waiting get resourceA!"); synchronized (resourceA){ System.out.println(Thread.currentThread() + "get resourceA!"); } } } }); threadA.start(); threadB.start(); } }
注意:不推荐使用,wait()时间太长,就会浪费资源,太短则可能不能够避免死锁!
参考:<<Java 并发编程之美>>