JavaEE多线程死锁


死锁

死锁: 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

一个线程加上锁之后,解不开了,僵住了…

死锁的几种情况

1️⃣一个线程一把锁,线程连续加锁两次~
如果这个锁是不可重入锁,就是死锁!
synchronized是可重入锁,没这个问题~

2️⃣两个线程,两把锁~

  • 钥匙锁车里了,车钥匙锁家里了
  • 一码通挂了,程序员回家需要出示一码通,但是要想出示一码通需要先回家修bug

这种死锁,可重入锁解决不了!

死锁代码:


public class TestDemo1 {


    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

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

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

        thread1.start();
        thread2.start();

    }
}

在这里插入图片描述
3️⃣ 多个线程多把锁
这个情况更容易死锁!!
描述这个死锁场景,有一个典型的模型,哲学家就餐问题~
在这里插入图片描述
这里有一个桌子,桌子上放着一碗意大利面,桌边坐着五个哲学家,一共只有五根筷子,分别放到两个人之间~
每个哲学家只会做两件事:

  1. 思考人生,啥都不干(线程阻塞了)
  2. 吃面条,先拿起左手的筷子,再拿起右手的筷子,然后吃面,吃饱了就放下了~

在这个过程中,什么时候吃面条,什么时候思考人生,是不确定的(线程随机调度)

大部分情况下,这个模型是可以良好运转的 ,不会死锁~
但是极端情况下,就会出现死锁!!

假设:
五个哲学家同时拿起左手的筷子!
在这里插入图片描述
这个时候再尝试拿右手的筷子时,就发现没有筷子可拿(拿不起来)
并且这几个哲学家互不相让,此时就会陷入僵局,谁都没办法完成吃面条这个操作~

如何解决死锁问题

明确了死锁问题,如何解决?
死锁的四个必要条件:

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

必要条件,只要打破其中一个,问题就迎刃而解了❗

1,2打破不了,是锁的基本特性~
想打破3取决于代码的写法,看看获取锁B的时候是否先释放锁A了? 有可能打破,但是还是要看具体的需求是否允许这么写~这里这个方案可能能行,但是不普适 ~

那只剩第4点了,而第4点也有把握打破!

只需要约定好加锁的顺序,就可以避免循环等待~

例如:

给锁编号

约定,加多个锁的时候,必须先加编号小的锁,后加编号大的锁,就可以有效地避免循环等待了~

比如把上面的代码按照上述约定修改一下:


public class TestDemo1 {


    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

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

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

        thread1.start();
        thread2.start();

    }
}

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

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总结

在这里插入图片描述

你可以叫我哒哒呀
本篇到此结束
“莫愁千里路,自有到来风。”
我们顶峰相见!
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值