一、什么是线程死锁?
线程死锁指的是多个线程互相阻塞,它们中间的一个或者多个在等待某个资源被释放。由于线程一直被阻塞,所以程序也就一直被阻塞。
如图所示,线程A持有资源2,线程B持有资源1,而此时线程A想请求资源1,线程B想请求资源2,导致两个线程互相等待而死锁。
简单代码实现:
public class TestClass {
Object resource1 = new Object(); // 资源1
Object resource2 = new Object(); // 资源2
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
// ...
synchronized (resource2) {
// ...
}
}
}, "线程1").start();
new Thread(() -> {
synchronized (resource2) {
// ...
synchronized (resource1) {
// ...
}
}
}, "线程1").start();
}
}
二、线程死锁有什么条件?
线程死锁需要满足四个必要条件:
- 互斥条件:该资源同一个时刻只能被一个线程持有
- 请求与保持条件:一个线程因请求资源而阻塞时,对已持有的资源保持不放
- 不剥夺条件:线程已持有的资源在未使用完之前不能强行被其他线程剥夺,只能等自己使用完之后才释放
- 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
三、如何预防线程死锁?
想要预防死锁,只需要破幻线程死锁的必要条件即可:
- 破坏请求与保持条件:一次性请求所有资源(要么都获得,要么都不获得)
- 破坏不剥夺条件:占用资源的线程再进一步请求其他资源时,如果请求不到,则主动释放已持有的资源
- 破坏循环等待条件:有序申请资源,反序释放资源
其中破坏循环等待条件的话,调整后的代码为:
public class TestClass {
Object resource1 = new Object(); // 资源1
Object resource2 = new Object(); // 资源2
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
// ...
synchronized (resource2) {
// ...
}
}
}, "线程1").start();
new Thread(() -> {
synchronized (resource1) {
// ...
synchronized (resource2) {
// ...
}
}
}, "线程1").start();
}
}
其实就是将线程1和线程2请求资源的顺序做了调整,都改为先请求资源1再请求资源2,这样就能防止两个线程分别占用对方想再请求的资源了。