美文网首页
死锁的实现与检测

死锁的实现与检测

作者: 费城的二鹏 | 来源:发表于2020-07-26 11:17 被阅读0次

    这是一道经典的面试题。今天的内容就是手动编写一个死锁代码,然后简要分析一下这个死锁代码。

    在讲正文前,先讲下 jdk 11 的一个新特性,可以直接运行单个的 Java 文件。而不用 javac 编译成 class,再运行。下文图中就是例子:

    java OddEvenPrinter.java 
    

    以后写 Demo 就不用非得用 ide 了,直接用记事本也是可以的跟 python 玩法就一样了。

    回归正文,锁存在的目的就是在多线程竞争时,保证同时只有一个线程能获取锁执行代码,其余线程等待,保证线程安全的方式。(好像说的稀碎)

    而死锁,就是两个或者多个线程互相持有别的线程需要的锁,而且都不主动释放的状态,我记得教科书上有死锁存在的必要条件:

    (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    (3)不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
    (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    我的描述基本就是上面四句话的意思。所以造一个最简单的死锁例子就是用两个线程,定义两个对象,并且线程1持有 monitorA 然后申请 monitorB,线程2持有 monitorB 然后申请 monitorA。

    public class Lock {
    
        private final Object monitorA = new Object();
        private final Object monitorB = new Object();
    
        public void printA() {
            synchronized (monitorA) {
                try {
                    // 暂停一下,让线程2有时间先获取 monitorB 的锁,否则线程1可能会直接执行完成
                    Thread.sleep(10);
                } catch (Exception e) {
                    //TODO: handle exception
                }
                synchronized (monitorB) {
                    System.out.println("printA");
                }
            }
        }
    
        public void printB() {
            synchronized (monitorB) {
                synchronized (monitorA) {
                    System.out.println("printB");
                }
            }
        }
    
        public static void main(String[] args) throws Exception {
            Lock printer = new Lock();
            Thread thread1 = new Thread(printer::printA, "thread-1");
            Thread thread2 = new Thread(printer::printB, "thread-2");
            thread1.start();
            thread2.start();
        }
    }
    

    代码中线程1在 synchronized (monitorA) 的里面添加了睡眠语句,这是为了等待线程 2 去执行完 synchronized (monitorB) 否则会导致线程 A 直接申请了 synchronized (monitorB) 两个线程就变成串行化执行了。

    这是简单例子,运行时会发现命令行什么都不输出,我们可以打印输出,看看停在了哪里,但是如果是生产环境发生死锁,我们没有条件知道到底哪里死锁,也基本不能尝试添加大量 log。那么如何检测死锁以及死锁的原因呢?祭出大杀器:JConsole

    在命令行输入 jconsole,然后选择需要监听哪个进程,这个如何确定哪个进程是你的,就算做课外作业了:)。

    线程 -> 检测死锁,可以看到,thread-1 和 thread-2发生死锁,thread-1 的并且在 Lock.java 14行,被锁住。

    image.png

    14行代码是什么呢?

    也就是可以看到 monitorB,这也符合我们的本意,因为它已经被 thread-2 给占用了。

    当生产环境发生了死锁时,我们也可以通过此方法检测死锁问题。

    by 费城的二鹏 2020.07.24 长春

    相关文章

      网友评论

          本文标题:死锁的实现与检测

          本文链接:https://www.haomeiwen.com/subject/cdhjlktx.html