Java关于死锁和如何打破死锁(sync版)

532 阅读2分钟

基础

死锁需要满足的条件:

  • 互斥:在一段时间内某个资源只能被一个线程所占用
  • 不可剥夺:一个线程不可以强制剥夺另一个线程所占用的资源
  • 循环等待:线程A在等待线程B手中持有的资源,线程B又在等待线程A手中持有的资源
  • 请求和保持:一个线程在等待另一个线程手中的资源,而此线程又不放弃自己手中的资源

手写死锁

```
// sync 版本
public static void main(String[] args) {
    // 首先声明两个资源
    Object object1 = new Object();
    Object object2 = new Object();

    // 启动线程 1
    new Thread(() -> {
        // 线程 1 通过 sync 独占锁拿到资源 1
        synchronized (object1){
            try {
                // 线程 1 睡眠一秒,让线程 2 拿到资源 2
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1尝试获取资源 object2 ");
            // 线程 1 此时尝试占有资源 2
            synchronized (object2){
                System.out.println("线程1获取资源 object2 成功 ");
            }
        }
    }, "线程1").start();

    new Thread(() -> {
        synchronized (object2){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2尝试获取资源 object1 ");
            synchronized (object1){
                System.out.println("线程2获取资源 object1 成功 ");
            }
        }
    }, "线程2").start();
}
```

解析:这里 object1 和 object2 都是互斥资源;当线程 1 拿到资源 1 之后请求拿到资源 2,这就是请求和保持;此时线程 1 和线程 2 相互循环等待。

问题:怎样打破这段死锁?

    • 其实 sync 的死锁打破比较简单,我们只需要把线程 1 获取到资源 1 之后的那段线程睡眠拿掉,让两个线程按照顺序去执行就ok了;
  • 另外一个办法是使用顺序锁的思想,代码如下
public static void main(String[] args) {

    Object object1 = new Object();
    Object object2 = new Object();

    new Thread(() -> {
        synchronized (object1){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1尝试获取资源 object2 ");
            synchronized (object2){
                System.out.println("线程1获取资源 object2 成功 ");
            }
        }
    }, "线程1").start();

    new Thread(() -> {
        // 线程 2 拿不到资源 1会在这里阻塞,直到线程 1 释放资源 1
        synchronized (object1){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2尝试获取资源 object1 ");
            synchronized (object2){
                System.out.println("线程2获取资源 object1 成功 ");
            }
        }
    }, "线程2").start();
}

`