死锁案例:
public class DeadLock {
public static String a = "a";
public static String b ="b";
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() {
synchronized(DeadLock.a) {
System.out.println("lock1 lock a");
try {
Thread.sleep(1000);
}catch(Exception e) {
}
synchronized(DeadLock.b) {
System.out.println("lock1 lock b");
}
}
}
}
class lock2 implements Runnable{
@Override
public void run() {
synchronized(DeadLock.b) {
System.out.println("lock2 lock b");
try {
Thread.sleep(1000);
}catch(Exception e) {
}
synchronized(DeadLock.a) {
System.out.println("lock2 lock a");
}
}
}
}
//当把a和b的值改为“”时候,并不会出现死锁的情况。
死锁的四大条件
1.互斥:所谓的互斥通俗理解就是一个资源在被一个进程占用以后,其它进程无法获取改资源,只能等待。
2.占有且等待:即一个进程占有了若干资源后,等待其他的资源
3.不可剥夺:被其他进程占有的资源,在该进程为执行完时,不会被其他进程所剥夺,只能占有的进程主动释放。
4.循环等待:存在循环的相互等待,是死锁的必要条件。
如何解决死锁?
预防死锁、避免死锁、检测死锁、解除死锁。
预防死锁(死锁预防是设法至少破坏产生死锁的四个必要条件之一)
在代码中防止死锁的产生,主要是破坏以上四大条件的其中一个。首先互斥条件无法破坏,不然会造成不可预期的影响。一.破坏占有等待的方案:
1.在获取到其所需要的所有资源之前,将其全都释放,如lock1在获取到a和b之前需要将这两个的锁都释放。
2.在对一个资源进行占有前,释放它所占用的资源。如lock1在获取到b资源之前,需要对a资源进行释放。
代码改为如下:
synchronized(DeadLock.a) {
System.out.println("lock1 lock a");
}
synchronized(DeadLock.b) {
System.out.println("lock1 lock b");
}
二.破坏不可剥夺
线程请求当前被另一个线程占有的一个资源,则操作系统可以抢占另一个线程,要求它释放资源。
三.破坏“循环等待”
为所有资源加上编号,申请资源只能按照资源的编号升序进行占有,保证不会出现循环
额外方法:可以通过lock对对象进行锁定的时间指定,防止超过死锁的无限循环等待
避免死锁
系统对进程的所需资源进行动态扫描,判断其为安全转态,还是不安全状态,根据状态进行确定是否分配。
检测死锁
通常使用jstack命令生成线程快照。
解除死锁
剥夺其他线程的资源,或是撤销该线程