手撕:死锁案例

产生死锁的条件:

  1. 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
  2. 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
  3. 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
  4. 循环等待:有一组等待进程 {P0, P1,..., Pn}P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,……,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。

这四个条件是产生死锁的 必要条件 ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

避免或预防死锁的方法:

  1. 破坏互斥条件
    • 尽量减少资源的独占性,使资源可以共享。例如,通过增加资源的可并发性来减少互斥资源的数量。
  2. 破坏持有并等待条件
    • 在进程开始时一次性请求所有需要的资源,或者在持有资源时不再请求新的资源。这样可以避免持有资源并等待其他资源的情况。
  3. 破坏不可剥夺条件
    • 允许资源的剥夺。当一个进程持有资源但无法获得其他所需资源时,可以暂时释放已经持有的资源,使其他进程可以利用这些资源。
  4. 破坏循环等待条件
    • 通过对资源进行编号,并要求进程按序号递增的顺序请求资源。这样可以破坏循环等待的形成。例如,规定所有进程在请求资源时必须先请求编号较小的资源,再请求编号较大的资源。

死锁示例:

public class DeadLockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            // 线程1先获取锁1,再获取锁2
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName() + "获取到锁1");
                try {
                    // 为了让线程2有机会获取锁2, 线程1休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                // 线程1获取锁1后,尝试获取锁2
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName() + "获取到锁2");
                }
            }
        }, "线程1").start();

        new Thread(() -> {
            // 线程2先获取锁2,再获取锁1
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + "获取到锁2");
                try {
                    // 为了让线程1有机会获取锁1, 线程2休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                // 线程2获取锁2后,尝试获取锁1
                synchronized (lock1) {
                    System.out.println(Thread.currentThread().getName() + "获取到锁1");
                }
            }
        }, "线程2").start();

        // 检测死锁
        while (true) {
            // 通过jps命令查看进程号
            // 通过jstack命令查看进程号对应的线程堆栈信息
        }
    }
}

通过 jpsjstack 检查死锁情况,控制台输出以下结果,可以看到发生了一处死锁:

Found one Java-level deadlock:
=============================
"线程1":
  waiting to lock monitor 0x000002644a60de00 (object 0x00000006214d59b0, a java.lang.Object),
  which is held by "线程2"
"线程2":
  waiting to lock monitor 0x000002644a60c400 (object 0x00000006214d59a0, a java.lang.Object),
  which is held by "线程1"

Java stack information for the threads listed above:
===================================================
"线程1":
        at com.example.dev.DeadLockDemo.lambda$main$0(DeadLockDemo.java:21)
        - waiting to lock <0x00000006214d59b0> (a java.lang.Object)
        - locked <0x00000006214d59a0> (a java.lang.Object)
        at com.example.dev.DeadLockDemo$$Lambda$14/0x0000000800066840.run(Unknown Source)
        at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)
"线程2":
        at com.example.dev.DeadLockDemo.lambda$main$1(DeadLockDemo.java:39)
        - waiting to lock <0x00000006214d59a0> (a java.lang.Object)
        - locked <0x00000006214d59b0> (a java.lang.Object)
        at com.example.dev.DeadLockDemo$$Lambda$15/0x0000000800066c40.run(Unknown Source)
        at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

Found 1 deadlock.
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值