线程的死锁


前言

什么是死锁?死锁是怎样产生的?怎么实现一个死锁?如何解决死锁?可能我们日常工作中很少用到这些相关内容,但是随着要求越来越高,这些东西不了解是不行的,下面对死锁做一个简单的记录。


一、什么是死锁?

先看一下百度百科对死锁的定义:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

简单来说,就是有两个线程A和B,线程A持有X资源,要去获取Y资源;而线程B持有Y资源,要去获取X资源,这两个线程都不会释放自己的资源而且都要获取对方持有的资源,从而造成的阻塞状态。

二、死锁是怎样产生的?

当然是我们写的了,嘿嘿。其实这个问题也可以换种说法,死锁的产生条件?死锁的产生有四个必要条件,我们一一列举分析:

  • 互斥条件:在一段时间内某资源只能由一个线程持有。如果其他线程请求该资源,只能等待,直至占用资源线程释放。
  • 请求与保持条件:线程至少持有一个资源,同时又去获取其他被占用资源,此时请求线程阻塞,而它持有的资源是不被释放的。
  • 不可剥夺条件:被线程持有的资源是不能被其他线程强制剥夺的,只能在资源释放后才能去获取。
  • 环路等待条件:在发生死锁时,必然存在一个线程-资源的循环等待链,等待的线程形成一个环路,造成永久阻塞。

三、怎样实现一个死锁?

有了上面的死锁产生的必要条件,根据对应的条件实现一个死锁还是比较简单的,我们只需要简单定义两个线程和两个资源,让它们之间满足上述条件即可,废话不多说,上代码:

public class DeadLockDemo {
	//资源A和B
    public static Object A = new Object();
    public static Object B = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (A){
                System.out.println(Thread.currentThread().getName()+"持有A,去获取B");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B){
                    System.out.println(Thread.currentThread().getName()+"获得B");
                }
            }
        },"A").start();

        new Thread(()->{
            synchronized (B){
                System.out.println(Thread.currentThread().getName()+"持有B,去获取A");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A){
                    System.out.println(Thread.currentThread().getName()+"获得A");
                }
            }
        },"B").start();
    }
}

上述代码的运行结果如下,打印了这两句话,程序一直处于运行状态。
在这里插入图片描述
还要注意一下,上面代码在获取各自资源之后休眠了一下,这样做是为了防止还没来得及将资源获取,一个线程就执行完毕了,是有概率出现这种现象的,如下图所示。
在这里插入图片描述

四、死锁的排查和预防?

我们可以利用JVM提供的相应命令去检测是否发生死锁,我们可以直接通过编译器(例如IDEA)的Terminal去输入命令,当然也可以通过cmd控制台去操作。
通过jps命令,输出当前的所有进程及其进程号:
这两种方式的结果是一样的
再通过jstack -l pid去查看对应的进程是否发生死锁,因为我们这里启动了死锁的demo,这里就直接看这个进程,结果如下:

D:\program\oa-parent>jstack -l 27212
2022-02-18 15:23:23
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode):

"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x000000000392b000 nid=0x13f8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE 

   Locked ownable synchronizers:
        - None

"B" #13 prio=5 os_prio=0 tid=0x000000001c0d1800 nid=0x642c waiting for monitor entry [0x000000001d20f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.css.oa.canteen.测试类.DeadLockDemo.lambda$main$1(DeadLockDemo.java:35)
        - waiting to lock <0x00000000d6f396a8> (a java.lang.Object)
        - locked <0x00000000d6f396b8> (a java.lang.Object)
        at com.css.oa.canteen.测试类.DeadLockDemo$$Lambda$2/125130493.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"A" #12 prio=5 os_prio=0 tid=0x000000001c0d0800 nid=0x4b14 waiting for monitor entry [0x000000001d10f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.css.oa.canteen.测试类.DeadLockDemo.lambda$main$0(DeadLockDemo.java:21)
        - waiting to lock <0x00000000d6f396b8> (a java.lang.Object)
        - locked <0x00000000d6f396a8> (a java.lang.Object)
        at com.css.oa.canteen.测试类.DeadLockDemo$$Lambda$1/1552787810.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

可以看到上面提示了线程状态:RUNNABLE,出现死锁的代码的位置,通过这些信息我们可以找到产生死锁的代码块。

那么在日常的开发过程种怎么避免死锁呢?既然死锁要上述的条件,那么避免死锁就破坏条件就行了(手动狗头),那么针对上述条件,可以做的操作如下:

  • 资源一次性分配,这样就不会再有请求了
  • 尽量调整获取锁的顺序,让其有序的获取资源
  • 允许线程剥夺被占用的资源,设置线程间的优先级

总结

上面只是对死锁做了一下概要的介绍,而实际工作中产生的问题还要具体分析。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雅俗共赏zyyyyyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值