什么是死锁?
死锁是多个线程之间因为抢夺不可剥夺资源而造成相互等待的现象。
举个栗子
现有两个字符串obj1,obj2,还有两个锁线程Lock1和Lock2。Lock1先锁住obj1,再锁住obj2,Lock2先锁住obj2,再锁住obj1。启动两个线程Lock1和Lock2,当某一时刻,Lock1锁住了obj1,请求obj2的资源,而同一时刻Lock2锁住了obj2,请求obj1,此时线程Lock1和Lock2就进入了死锁的状态,因为两个线程进入了相互等待的状态,Lock1等待Lock2所持有的obj2,Lock2等待Lock1所持有的obj1。
代码如下:
public class DeadLock {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args){
Thread a = new Thread(new Lock1());
Thread b = new Thread(new Lock2());
a.start();
b.start();
}
}
class Lock1 implements Runnable{
@Override
public void run(){
try{
System.out.println("Lock1 running");
while(true){
synchronized(DeadLock.obj1){
System.out.println("Lock1 lock obj1");
Thread.sleep(3000);//获取obj1后先等一会儿,让Lock2有足够的时间锁住obj2
synchronized(DeadLock.obj2){
System.out.println("Lock1 lock obj2");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class Lock2 implements Runnable{
@Override
public void run(){
try{
System.out.println("Lock2 running");
while(true){
synchronized(DeadLock.obj2){
System.out.println("Lock2 lock obj2");
Thread.sleep(3000);
synchronized(DeadLock.obj1){
System.out.println("Lock2 lock obj1");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
死锁的产生的原因
1.系统资源不足
系统中不可剥夺的资源数量不能满足多个进程运行的需要,使进程在运行过程中因争夺资源而进入僵局。
2.进程推进顺序不合理
如进程P1和P2分别拥有资源R1和R2,P2要申请R1,P1要申请R2。
死锁产生的四个必要条件
1.互斥
一个资源只能被一个进程使用
2.不可剥夺
一个资源被一个进程使用,在该进程使用的时候,该资源不可被剥夺,只有该进程释放后才能被其他资源使用。
3.请求与保持
进程已经拥有至少一个资源,同时他又请求另一个资源,而这个资源被其他进程占用了,此时请求进程被阻塞,该进程手持已经拥有的资源不放手。
4.循环等待
若干进程之间形成首位相接形成循环等待资源的关系。
例如进行A 等待进程B的资源,进程B等待进程C的资源,进程C等待进程A的资源。
预防死锁(破坏四个必要条件)
1.破坏请求与保持:将资源一次性分配
2.破坏不可剥夺:当进程请求资源被阻塞时,进程释放资源
3.破坏环路等待:将资源从小到大编号,进程按编号递增顺序请求资源
4.破坏互斥:一般来说互斥条件是不能破坏的所以破坏前三条即可
避免死锁
银行家算法,在进程分配资源时先计算这个分配是否安全,如果不安全就不分配,安全就分配。
1.设置加锁顺序:
2.设置加锁时限:在进成功获取锁的时候,添加一个获取锁的时限,如果超过了,就放弃获取锁
3.死锁检测:使用Jconsole
解除死锁
剥夺资源:从其他进程中剥夺一定数量的资源给死锁进程。
撤销进程:撤销代价最小的进程或死锁进程,直到有足够的资源可用。