JavaEE——死锁(看图轻松理解哲学家就餐问题)

JavaEE传送门

JavaEE

JavaEE——JUC

JavaEE——线程安全的集合类


目录

  • 死锁
    • 1. 死锁是什么
    • 2. 如何避免死锁
      • 哲学家就餐问题


死锁

1. 死锁是什么

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。简单来说就是: 死锁, 一个线程加上锁之后, 解不开了, 僵住了.

场景1: 一个线程, 一把锁,

线程连续加锁两次, 如果这个锁是不可重入锁, 就会变成死锁. (synchronized 是可重入锁, 没有这个问题)

场景2: 两个线程两把锁

比如: 家里钥匙锁车里了, 车钥匙锁家里了.

public class Test {
    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(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("t1 尝试获取 locker2");
                synchronized (locker2) {
                    System.out.println("t1 获取两把锁成功!");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2 尝试获取 locker2");
            synchronized (locker2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 尝试获取 locker1");
                synchronized (locker1) {
                    System.out.println("t2 获取两把锁成功!");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

运行结果展示, 无法再获取到锁.

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第1张图片

场景3: 多个线程多把锁 (更容易死锁)

哲学家就餐问题

有五个哲学家, 五根筷子. 五根筷子分别放到每两个哲学家之间.

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第2张图片

每个哲学家只会做两件事:

  1. 思考, 什么都不做(线程阻塞了)
  2. 吃面条, 先拿起左手的筷子, 再拿起右手的筷子, 吃面条, 吃完放下筷子.

大部分情况, 这个模型是可以良好运行的, 不会发生死锁.

但如果五个哲学家, 在同一时间, 同时拿起左手的筷子. 再拿右手的筷子时, 发现右手边没有筷子. 并且这几个哲学家互不相让, 此时就会陷入僵局.


2. 如何避免死锁

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

  1. 互斥使用. 锁A 被线程1 占用, 线程2 就不用了.
  2. 不可抢占. 锁A 被线程1 占用, 线程2 不能把锁A 给抢过来, 除非线程1 主动释放.
  3. 请求和保持. 有多把锁, 线程1 拿到锁A 之后, 不想释放锁A , 还想拿到一个锁B.
  4. 循环等待. 线程1 等待线程2 释放锁, 线程2 释放锁等待线程1 释放锁.

所以我们只要打破以上四种的其中一个, 就可以避免死锁

  1. 互斥使用, 不可打破, 是锁的基本特性
  2. 不可抢占, 不可打破, 是锁的基本特性
  3. 请求和保持, 有可能打破, 取决于代码的写法, 看获取锁B 的时候是不是先释放锁A. (主要看需求场景是否允许这么写)
  4. 循环等待, 有把握打破! 只要约定好加锁的顺序, 就可以打破循环等待.

比如: 给锁编号, 约定, 加多个锁的时候, 必须先加编号小的锁, 后加编号大的锁. 就可以有效避免循环等待.


哲学家就餐问题

我们给筷子进行编号, 先拿编号小的, 后拿编号大的. (不再是先拿左手再拿右手)

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第3张图片

假设这时五个哲学家又同时伸手拿第一支筷子(尝试获取第一把锁)

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第4张图片

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第5张图片

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第6张图片

继续向下, 直到吃完, 开始吃面.

JavaEE——死锁(看图轻松理解哲学家就餐问题)_第7张图片

还有一个解决方案, 为 “银行家算法”, 就是把所有的资源统一进行统筹分配, 也能避免死锁. 但这个方法最大的问题就是比较复杂, 不太适合实际开发.


(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!
在这里插入图片描述
加粗样式

这里是Gujiu吖!!感谢你看到这里
祝今天的你也
开心满怀,笑容常在。

你可能感兴趣的:(JavaEE,java-ee,java)