多线程|死锁的成因和解决方案

死锁

什么是死锁

死锁就是锁对象的循环等待问题

public static void main(String[] args) {
    Object lock1 = new Object();
    Object lock2 = new Object();

    Thread t1 = new Thread(() -> {
        synchronized (lock1) {
            System.out.println("我想看t2表演个才艺之后再开始学习~");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (lock2) {
                System.out.println("舒服了,我现在开始学习了");
            }
        }
    }, "t1");
    Thread t2 = new Thread(() -> {
        synchronized (lock2) {
            System.out.println("等t1学习结束之后,我再给他来个才艺展示~");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (lock1) {
                System.out.println("可以, 我给你展示一下我的才艺~");
            }
        }
    }, "t2");
    t1.start();
    t2.start();
}
// 执行结果
等t1学习结束之后,我再给他来个才艺展示~
我想看t2表演个才艺之后再开始学习~

t1线程在等lock2释放锁,t2线程在等lock1释放锁,结果谁都拿不到锁,这就是死锁

我们通过jconsole命令查看以下t1和t2线程的状态(点击“检测死锁”):

t1线程的状态:此时t1想要获取的锁正被t2线程持有

image-20220713210324799

t2线程的状态:此时t2想要获取的锁正被t2线程持有:

image-20220713210342172

有个非常著名的“哲学家进餐问题” - 就是一个死锁问题,锁的循环等待问题

死锁的避免

死锁产生的四个必要条件:

  • 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样 就形成了一个等待环路

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

但是前面三个条件就是互斥锁的概念,没法破坏,其中最容易破坏的就是 “循环等待”,即不让多个线程获取资源你不要成环即可

public static void main(String[] args) {
    Object lock1 = new Object();
    Object lock2 = new Object();
    Thread t1 = new Thread(() -> {
        synchronized (lock2) {
            System.out.println("我现在开始学习了");
            synchronized (lock1) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("学习结束! 我想看t2表演~");
            }
        }
    }, "t1");
    Thread t2 = new Thread(() -> {
        synchronized (lock2) {
            System.out.println("等t1学习结束之后,我再给他来个才艺展示~");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (lock1) {
                System.out.println("可以, 我给你展示一下我的才艺~");
            }
        }
    }, "t2");
    t1.start();
    t2.start();
}
// 执行结果
我现在开始学习了
学习结束! 我想看t2表演~
等t1学习结束之后,我再给他来个才艺展示~
可以, 我给你展示一下我的才艺~

版权声明:本文为m0_61925333原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。