死锁
互相等待对方释放锁
死锁的条件
- 互斥条件
资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用。 - 不可剥夺条件
进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放 - 请求和保持条件
进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源 - 循环等待条件
若干进程间形成首尾相接循环等待资源的关系
死锁处理方法
预防死锁
- 破坏“占有并等待”条件:就是在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,阻止进程在持有资源的同时申请其他资源
方法一:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或什么也不给它。这是所谓的 “ 一次性分配”方案
方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,须先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R - 破坏“不可抢占”条件:允许对资源实行抢夺
方法一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源
方法二:如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两个进程的优先级都不相同的条件下,方法二才能预防死锁 - 破坏“循环等待”条件:将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。这样做就能保证系统不出现死锁
避免死锁
- 所有获取资源的线程按照一定的顺序获取资源
- 银行家算法,是指在分配资源之前先进行安全性检查,资源分配后是否会导致系统死锁。如果会死锁,则不分配,否则就分配
解除死锁
一旦检测出死锁,就应立即釆取相应的措施,以解除死锁
- 资源剥夺法:挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程
- 撤销进程法:强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
- 进程回退法:让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点
手写死锁-java
class Server implements Runnable {
public String userName;
public Object object1=new Object();
public Object object2=new Object();
public void setUserName(String userName){
this.userName=userName;
}
public void run(){
if(userName.equals("a")){
synchronized (object1){ //a已获取锁
System.out.println("userame: "+userName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//synchronized 是可重入锁
synchronized (object2){ //等待b释放锁
System.out.println("first--object1 end--object2");
}
}
}
if(userName.equals("b")){
synchronized (object2) { //b已获取锁
System.out.println("userame: " + userName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1) { //等待a释放锁
System.out.println("first--object2 end--object1");
}
}
}
}
}
public class DealThread {
public static void main(String[] args) {
try {
Server server1=new Server();
server1.setUserName("a");
Thread thread=new Thread(server1);
thread.start();
Thread.sleep(1000);
server1.setUserName("b");
Thread thread1=new Thread(server1);
thread1.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Java死锁查看分析方法
Jconsole
jconsole是jdk自带的工具,用它来监控Java的运行情况
- 打开cmd然后输入jconsole打开
- 连接到需要查看的进程
- 打开线程选项卡,然后点击左下角的“检测死锁”, jconsole就会给我们检测出该线程中造成死锁的线程,点击选中即可查看详情
从上图中可看出:
在线程Thread-1中,从状态可以看出,它想申请java.lang.Object@6b9476fe这个资源,但是这个资源已经被Thread-0拥有了,所以就堵塞了
Thread-1一直等待java.lang.Object@6b9476fe资源,而Thread–0一直也等待java.lang.Object@474a97ed资源,于是这两个线程就这么僵持了下去,造成了死锁
Jstack
Jstack是JDK自带的命令行工具,主要用于线程Dump分析
- 先用Jps来查看java进程id(或者Linux的ps命令)
- jstack输出线程dump信息到文件
- 查看log文件,然后进行分析
其中有一行是at Server.run(DeadThread.java:20),说明Thread-0是在DeadThread类的20行处发生死锁…剩余内容省略