死锁
一、死锁的定义
多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
死锁,是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
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();
}
}
四、死锁采取策略
- 鸵鸟政策:采取不理睬策略
- 预防策略: 破坏产生死锁的四个必要条件
- 避免策略:精心的分配资源,动态的回避死锁
(使用银行家算法) - 检测和解除:发生死锁后及时检测出,采取措施解除(撤销进程或剥夺资源)
代码修改如下
Thread t2 = new Thread(new Test(false));
改为:
Thread t2 = new Thread(new Test(true));
//现在应该不会出现死锁了,因为线程a和线程b都是先对locka,然后再对lockb,当t1启动后,锁住了locka,而t2也启动后,只有当t1释放了locka后t2才会执行,从而有效的避免了死锁。