0
点赞
收藏
分享

微信扫一扫

ESlint

juneyale 2023-09-09 阅读 52

1.死锁是指两个或多个线程互相等待对方释放所持有的资源,从而导致进程无法继续执行的一种情况。具体来说,死锁发生时线程会进入一个永久等待的状态,无法继续执行并最终导致程序无响应或崩溃。

2.产生死锁的条件,通常被称为死锁的四个必要条件,包括:

  1. 互斥条件(Mutual Exclusion):至少有一个资源被多个线程独占,也就是说一个资源同时只能被一个线程占用。
  2. 请求与保持条件(Hold and Wait):线程已经持有了至少一个资源,并且在等待获取其他线程占有的资源。
  3. 不可剥夺条件(No Preemption):已经获得的资源不能被其他线程抢占,只能由占有它的线程显示地释放。
  4. 循环等待条件(Circular Wait):多个线程形成一个循环等待的等待链,即每个线程都在等待下一个线程所持有的资源。

只有当上述四个条件同时满足时,死锁才可能发生。

 public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1:锁住offer1");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("Thread 1:锁住offer2");
                }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2:锁住offer2");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("Thread 2:锁住offer1");
                }
        });

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

7c7be128d11d4408a42624aab68a8cfe.png

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:锁住了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("小一:锁住了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("小一:锁住机考第一名");
                }
            }
        });

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

6187c605ae9847b1b2d39a5eccaab3af.png

又是你俩,干啥呢,死锁了呀~offer不止一个呀,就不能友好相处,快乐的玩耍同时都拿到offer么

1. 先使用javac -encoding UTF-8 X.java, 来生成class文件

2. javap -verbose X.class 反编译

3.从反编译的指令来看,这里应该是操作系统或者jvm虚拟机检查到了这是个死锁,强制中断了,在使用synchronized作为锁的时候,我们知道是有monitorenter monitorexit 这一对指令的,但是这里就没有看到

4. 下载了个idea的插件jclasslib 来查看,看字节码指令是否一致

5.可以看到这里是有这一对锁指令的,这里稍微解释下上写字节码指令的含义

0 aload_0:加载索引为0的引用到操作数栈,通常用于加载实例方法的隐式参数,即this。

1 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

2 astore_2:将栈顶的引用类型数值存储到局部变量表的索引为2的位置。

3 monitorenter:进入同步块前获取锁。

4 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值,即标准输出流PrintStream对象。

7 ldc #12 <小一:锁住了笔试第一名>:将常量池中索引为12的String类型常量加载到操作数栈。

9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

12 ldc2_w #13 <1000>:将常量池中索引为13的long类型常量加载到操作数栈。

15 invokestatic #15 <java/lang/Thread.sleep : (J)V>:执行Thread类的静态方法sleep,其中参数为栈顶的long类型常量。

18 goto 33 (+15):无条件跳转到字节码指令33,即跳过下方的指令。

21 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

22 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值。

25 ldc #17 <小一:被中断,释放笔试资源>:将常量池中索引为17的String类型常量加载到操作数栈。

27 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

30 aload_2:加载局部变量表中索引为2的引用类型数值到操作数栈。

31 monitorexit:退出同步块,释放锁。

32 return:返回void类型的值,并结束当前方法。

33 aload_1:加载局部变量表中索引为1的引用类型数值到操作数栈。

34 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

35 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

这里的指令

第9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。执行了catch中的打印日志,说明被执行中断了,后面goto 33 跳到33行的指令

6.此外我另外写了同步方法,来看看

从这里看出,它是有加锁的,第20行多了一个monitorexit,这就是防止异常强制释放锁,也就是synchronized能自动释放锁的保障

退一步想阔天空,你好我好大家好

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:获得了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:获得了笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("小一:获得了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("小一:获得了机考第一名");
                }
            }
        });

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

85cc84572eba4e8b85376390faadb889.png

只要稍微改下获取资源的顺序,半生跟小一就分别都获取了机考,笔试第一名,都收到了XX大厂offer

打破死锁的方式有多种,只要四个死锁的必要条件去其一就可以了

举报

相关推荐

0 条评论