什么是线程死锁?如何避免?


一、什么是线程死锁?

线程死锁指的是多个线程互相阻塞,它们中间的一个或者多个在等待某个资源被释放。由于线程一直被阻塞,所以程序也就一直被阻塞。

如图所示,线程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,这样就能防止两个线程分别占用对方想再请求的资源了。


文章作者: GaryLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GaryLee !
  目录