死锁:两个或多个线程互相持有对方需要的锁而导致这些线程全部处于永久阻塞状态。如:线程A持有对象1的锁,等待对象2的锁;线程B持有对象2的锁,等待对象1的锁。
死锁产生的条件:
1、互斥条件:资源不能被共享,只能由一个进程使用
2、请求与保持条件:进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
3、不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
4、循环等待条件(Circular wait) :若干个进程形成环形链,每个都占用对方申请的下一个资源。
code:
public class DeadLock implements Runnable{
//注意,这里需要定义为static,否则t1和t2两个线程中的o1,o2均为新创建的,也就是此o1非彼o1,o2也一样,即不存在资源冲突,不会发生死锁。字符串除外,因为有字符串池
private static Object o1=new Object();
private static Object o2=new Object();
private int flag;
public DeadLock(int flag){
this.flag=flag;
}
@Override
public void run() {
if(flag==0){
synchronized (o1){
System.out.println("t1 Get the lock of o1");
try{
Thread.sleep(1000);
}
catch (InterruptedException ex){
ex.printStackTrace();
}
synchronized (o2){
System.out.println("t1 Get the lock of o2");
}
}
}
else{
synchronized (o2){
System.out.println("t2 Get the lock of o2");
try{
Thread.sleep(1000);
}
catch (InterruptedException ex){
ex.printStackTrace();
}
synchronized (o1){
System.out.println("t2 Get the lock of o1");
}
}
}
}
public static void main(String[]args){
DeadLock D1=new DeadLock(0);
DeadLock D2=new DeadLock(1);
Thread t1=new Thread(D1);
Thread t2=new Thread(D2);
t1.start();
t2.start();
}
}
解决死锁的三种方案:
1.资源排序
如上诉代码中将else{}中的o1和o2调换顺序即可。
2.加锁时限
如果一个线程没有在指定的时间期限内获取到锁,则结束当前线程并释放掉已获得的锁。终止线程的方法:stop()会释放掉锁但易导致数据不一致。suspend()终止线程但不会释放掉锁。
3.死锁检测
处理死锁的方法:
1、鸵鸟算法:
2、银行家算法:
所谓银行家算法,是指在分配资源之前先看清楚,资源分配后是否会导致系统死锁。如果会死锁,则不分配,否则就分配。
按照银行家算法的思想,当进程请求资源时,系统将按如下原则分配系统资源:
解决死锁的策略
对待死锁的策略主要有:
(1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
(2) 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
(3) 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
(4) 死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。