(1)死锁案例
/*** 一个简单的死锁类
* main方法中启动两个线程,分别调用methodA和methodB方法
* methodA方法首先获取到a对象的锁,睡眠1秒钟
* 此时methodB方法执行获取到b对象的锁,睡眠1秒
* 此时methodA需要去获取b对象的锁才能继续执行,但是b锁没有被释放无法获取到
* 此时methodB需要去获取a对象的锁才能继续执行,但是a锁没有被释放无法获取到
* 从而两者相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。*/
public classDeadLock {private static String a = "1";private static String b = "2";public voidmethodA() {synchronized(a) {
System.out.println("我是A方法中获得到了A锁");try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}synchronized(b) {
System.out.println("我是A方法中获取到b锁");
}
}
}public voidmethodB() {synchronized(b) {
System.out.println("我是B方法中获得到了b锁");try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}synchronized(a) {
System.out.println("我是B方法中获取到a锁");
}
}
}public static voidmain(String[] args) {new Thread(() ->{
d.methodA();
}).start();new Thread(() ->{
d.methodB();
}).start();//new Thread(new Runnable() {//@Override//public void run() {//d.methodB();//}//}).start();}
}
(2)死锁
1、死锁的定义:进程中的每一个线程都在等待该组线程释放资源以此来运行自己,则该组进程是死锁的。
2、死锁产生的条件:
1)互斥条件:一段时间内某资源只由一个线程占用。如果此时还有其它线程请求资源,则请求者只能等待,直至占有资源的线程用毕释放。
2)请求和保持条件:线程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它线程占有,此时请求线程阻塞,但又对自己已获得的其它资源保持不放。
3)不可剥夺条件:线程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)循环且等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
注意:打破四个必要条件之一就能有效预防死锁的发生。
3、死锁产生的原因
1)竞争资源引起进程死锁:
a、竞争不可剥夺资源:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞
b、竞争临时资源:如缓冲区内的消息
2)进程间推进顺序非法
4、解决死锁的方法
1)预防死锁:
a、打破互斥条件:即允许进程同时访问某些资源。但是有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。
b、打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
c、打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
d、打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
2)避免死锁:
银行家算法:当进程请求一组资源时,假设系统同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。
3)解除死锁:先检测是否有死锁,若有则解除死锁。
a、检测死锁:利用Jstack命令打印Java堆栈信息来定位线程出现长时间停顿的原因
b、解除死锁:若发现有线程死锁后,应立即把它从死锁状态中解除,方法如下:
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤销线程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用即死锁状态解除为止;