死锁是什么
最简单的解释:两个人都想吃饭,但是只有一双筷子。A,B同时抢到了一只筷子,都想强一双,并且死不放手。结果就是A,B双双饿死。
因为资源有限,而想要资源的人过多,导致资源不足,互相争抢,最后产生了无穷尽的等待。
死锁不一定只会发生在锁的争抢上,从宏观上来讲,只要是有限的资源在使用时都可能发生死锁。这个概念最早是和操作系统中的PV原语同时出现,在操作系统中有一个著名的银行家算法可以绝对的避免死锁。
如:在使用redis,文件系统,mysql事务时都可能产生死锁。
死锁的必要条件
死锁发生了往往就会导致程序不可用,或者系统级别的崩溃。因此防范死锁是很重要的,但死锁也不是很容易就会发生的,它有如下四个必要条件。
- 互斥条件: 一个资源每次只能被一个进程使用; 即资源被A拿了,就不能再被B拿走;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放; 即资源被A拿了,除非不用了,否则A不会归还。
- 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺; 即资源被A拿了,除非A归还,否则谁也抢不了。
- 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系; 即资源出现了不足的情况,且没人归还。每个人都没拿到足够多的资源,A祈祷B赶紧还了,B祈祷A,形成循环。
避免死锁有哪些方式
而如果要避免死锁的发生我们只需要破坏以上四个条件的其中之一即可。
如:
获取锁时设置超时时间(破坏条件2);
共享锁(破坏条件1);
MySQL死锁恢复机制(破坏条件3);
银行家算法(破坏条件4);
死锁的Java实现
下面是死锁发生的场景之一。
有两个线程A and B,A执行了一段任务想等B执行完成再执行,同样如果B如果执行了一半想等等A。那么就会一直等到天长地久QWQ。
public static void main(String[] args) {
test2();
}
public static void test2(){
class A{
Thread threadA = null;
Thread threadB = null;
public void doSome(){
threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A start");
try {
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A end");
}
});
threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B start");
try {
threadA.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B end");
}
});
threadA.start();
threadB.start();
}
};
new A().doSome();
}
你会发现运行了Start 便没有了end。那么我们成功的写了一个死锁。