什么是死锁
首先死锁的产品肯定是发生在两个或者两个以上的线程之间,因为线程请求独占资源时,由于资源被对方占用,自身被挂起等待;从而造成相互等待,在没有干预时,将会一直等待下去的现象称为死锁。
死锁产生的原因
死锁的产生都是由于多个线程之间资源竞争造成的,那么需要什么样的条件,多个线程之间才会造成死锁的现象呢?下面是造成死锁产生的四个条件:
1、资源是互斥的;
也就是说线程请求的资源是互斥的,也就是说都是独占锁,只要该资源被某个线程持有后,其他线程无法请求到该资源,自能被放入等待队列,然后线程被挂起;直到持有该资源的线程释放资源后,被挂起的线程会重新去请求该资源。
2、某线程持有了一个独占资源,并且还需要请求另一独占资源;
也就是说某一个线程它已经获取了一个独占的资源,持有了一个独占的锁,但是由于业务需要,他还需要再请求另一个独占的资源;如果正好这个资源被别的线程持有,它就只能等待,但是并不会释放它持有的其他资源;
3、线程持有的资源只能由自己释放;
也就是说当某线程获取某独占资源时,这个资源已经是自己持有的了,当其他线程获取的时候,只要自己没有释放,其他线程是无法获取的,其他线程也不能主动去释放当前线程持有的资源;
4、多个线程被挂起等待资源时,造成了循环等待的情况;
也就是说由于某个线程持有了一个资源,这个时候它需要请求另一个资源,另一个资源刚好被另一个线程持有,而另一个线程又需要请求这个资源;比如线程 A,它持有一个资源 A 这个时候由于业务需要,它需要请求资源 B,而资源 B 被线程 B 持有,而线程 B 刚好又需要请求资源 A,于是就造成了循环等待的情况了。
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread threadA = new Thread(() -> {
synchronized (lockA) {
System.out.println("threadA acquired the lockA");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("threadA waiting for lockB");
synchronized (lockB) {
System.out.println("threadA acquired the lockB");
}
}
});
Thread threadB = new Thread(() -> {
synchronized (lockB) {
System.out.println("threadB acquired the lockB");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("threadB waiting for lockA");
synchronized (lockA) {
System.out.println("threadB acquired the lockA");
}
}
});
threadA.start();
threadB.start();
}
结果
threadA acquired the lockA
threadB acquired the lockB
threadA waiting for lockB
threadB waiting for lockA
以上示例就产生了死锁,它达到了以上所说的四个必要的条件。
如何避免死锁
要想避免死锁的产生,就需要根据死锁产生的条件来避免,所以就需要破坏死锁产生的条件。而从上面的四个条件来说,第一个和第三个资源是不能改变的,我们只能改变第二个和第四个条件,所以有两种方式。
方式1:
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread threadA = new Thread(() -> {
synchronized (lockA) {
System.out.println("threadA acquired the lockA");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("threadA waiting for lockB");
synchronized (lockB) {
System.out.println("threadA acquired the lockB");
}
});
Thread threadB = new Thread(() -> {
synchronized (lockB) {
System.out.println("threadB acquired the lockB");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("threadB waiting for lockA");
synchronized (lockA) {
System.out.println("threadB acquired the lockA");
}
});
threadA.start();
threadB.start();
}
方式2:
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread threadA = new Thread(() -> {
synchronized (lockA) {
System.out.println("threadA acquired the lockA");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("threadA waiting for lockB");
synchronized (lockB) {
System.out.println("threadA acquired the lockB");
}
}
});
Thread threadB = new Thread(() -> {
synchronized (lockA) {
System.out.println("threadB acquired the lockB");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("threadB waiting for lockA");
synchronized (lockB) {
System.out.println("threadB acquired the lockA");
}
}
});
threadA.start();
threadB.start();
}
结果
threadA acquired the lockA
threadA waiting for lockB
threadA acquired the lockB
threadB acquired the lockB
threadB waiting for lockA
threadB acquired the lockA
方式1,破坏了第二个条件;方式2,由于改变了线程 B 加锁的顺序,因此它破坏了第四个条件。
总结
死锁是在实际工作中很容易产生的问题,所以要清楚死锁的产生条件,避免在工作中创造产生死锁的条件,当实际工作有类似的需求时,要破坏死锁产生的条件,从而避免死锁。
网友评论