【Java|多线程与高并发】死锁以及哲学家吃饭问题


在这里插入图片描述

1. 什么是死锁

死锁(Deadlock)是多线程编程中的一个常见问题,指的是两个或多个线程相互等待对方释放资源,导致程序无法继续执行的状态。

在一种典型的死锁情况中,有两个或多个线程,每个线程都在持有一个资源的同时试图获得另一个线程持有的资源。当两个线程都在等待对方释放资源时,它们将永远无法继续执行,产生了死锁。

代码示例:

public class Demo26 {
    
    
    public static void main(String[] args) {
    
    
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(() ->{
    
    
            System.out.println("t1获取locker1");
            synchronized (locker1) {
    
    
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
    
    
                    System.out.println("t1获取locker2");
                }

            }

        });
        Thread t2 = new Thread(() ->{
    
    
            System.out.println("t2获取locker2");
            synchronized (locker2) {
    
    
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                synchronized (locker1) {
    
    
                    System.out.println("t2获取locker1");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

运行结果:
在这里插入图片描述

解释: t1对locker1加锁,t2对locker2加锁. 然后t1要获取locker2,t2要获取locker1. 但是t1和t2并没有执行完代码,并不会释放锁,需要获取对方的锁之后,才会释放锁. 因此它们就"僵"住了. 代码执行不下去了.

之前看到过一句话也能很好的理解这个问题,比如你去面试,面试官问你一个问题. 你回答说"你先给我发offer,我就回答问题". 面试官说"你先回答问题,我再给你发offer".

2. 哲学家吃饭问题

死锁还有一个很经典的问题,就是哲学家吃饭问题.

哲学家吃饭问题: 有五位哲学家围坐在一张圆形餐桌周围,每个哲学家需要交替进行思考和进餐。在餐桌上有五只筷子,每个哲学家的左右两边分别放有一只筷子。哲学家只能同时使用自己左右两边的筷子来进餐,而当一个哲学家使用筷子时,其他哲学家必须等待。

如图所示:
在这里插入图片描述
上述可能出现死锁的原因是: 两个及以上的哲学家可能会去争夺同一根筷子.

极端情况就是五个哲学家同时拿起自己左手边的一根筷子,想去拿右边的筷子,却发现已经被拿了. 谁也吃不到饭,无法释放筷子(锁).

解决上述问题的关键就是 如何解决死锁问题.

3.如何解决死锁

死锁的四个必要条件:

  1. 互斥使用: 资源不能同时被多个线程持有,只能被一个线程独占。
  2. 不可抢占: 一个线程获得资源后,不能被其他线程强制性地抢占。
  3. 请求和保持: 一个线程持有一个资源的同时,又请求另一个线程持有的资源。
  4. 循环等待: 若干个线程之间形成一种循环等待资源的关系。

避免死锁,只需要让其中任意一个条件不满足即可.

其中1和2是锁的基本特性,无法改变. 因此只能从条件3和条件4方面下手.

而条件3虽然可能能行,但是可能会带来新的问题.因此需要从条件4上打破

可以给上述的筷子设置编号. 设置好加锁的循序,每次先给编号小的进行加锁就可以解决上述问题.

以哲学家吃饭的极端情况为例:

默认情况下,一个人一根筷子.

在这里插入图片描述

对加锁的顺序进行设置(先拿序号小的).
在这里插入图片描述
这里的5号哲学家不会拿筷子.因为对于5号哲学家来说,5比1大,5号哲学家要拿1号筷子. 但1号筷子已经被拿了,所以5号哲学家就进入"阻塞等待"了. 那么4号玩家就可以继续拿起5号筷子进行吃饭,然后进行释放筷子(锁),接着是3号,2号,1号.等1号哲学家释放完1号筷子,那么此时5号哲学家就可以拿起1号筷子和5号筷子进行吃饭了

感谢你的观看!希望这篇文章能帮到你!
Java专栏在不断更新中,欢迎订阅!
“愿与君共勉,携手共进!”

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_63463510/article/details/130586156