死锁

一、死锁的定义

多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

 死锁,是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了。导致了死锁。这是最容易理解也是最简单的死锁的形式。但是实际环境中的死锁往往比这个复杂的多。可能会有多个线程形成了一个死锁的环路,比如:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1,这样导致了一个锁依赖的环路:T1依赖T2的锁L2,T2依赖T3的锁L3,而T3依赖T1的锁L1。从而导致了死锁。

二、死锁产生的必要条件

产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

  • (1)互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
  • (2)不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
  • (3)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
  • (4)循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求

写一个死锁程序

/*写一个死锁程序 DeadLockTest*/
class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
	synchronized(MyLock.locka) {	
			System.out.println("if locka");
			synchronized (MyLock.lockb)
			{
				System.out.println("if lockb");
			}    }
		}
		
		else
		{
			synchronized(MyLock.lockb)
			{
				System.out.println("else lockb");
				synchronized(MyLock.locka)
				{
					System.out.println("else locka");
				}
			}
		}
	}
}

class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}
public class DeadLockTest {
	
	public static void main(String[] args) {
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
	

}

四、死锁采取策略

  1. 鸵鸟政策:采取不理睬策略
  2. 预防策略: 破坏产生死锁的四个必要条件
  3. 避免策略:精心的分配资源,动态的回避死锁
    (使用银行家算法)
  4. 检测和解除:发生死锁后及时检测出,采取措施解除(撤销进程或剥夺资源)

代码修改如下

Thread t2 = new Thread(new Test(false));

改为:

Thread t2 = new Thread(new Test(true));
//现在应该不会出现死锁了,因为线程a和线程b都是先对locka,然后再对lockb,当t1启动后,锁住了locka,而t2也启动后,只有当t1释放了locka后t2才会执行,从而有效的避免了死锁。