欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java基础:一个100%会发生死锁的程序

程序员文章站 2022-03-30 21:19:11
    多线程是Java工程师进阶所必须掌握的一项技能,也是面试中绕不过的一个环节,而死锁又是多线程同步失败的经典案例,对于复杂的系统,死锁是很难通过代码层面来做静态检测和排查的,所以有的面试官会从反向出发,让你手写一个死锁程序。   &n ......

    多线程是java工程师进阶所必须掌握的一项技能,也是面试中绕不过的一个环节,而死锁又是多线程同步失败的经典案例,对于复杂的系统,死锁是很难通过代码层面来做静态检测和排查的,所以有的面试官会从反向出发,让你手写一个死锁程序。
    先来看一个网络上常见的死锁程序(可能存在问题):

public class deadlocktest {

    private static object lock1 = new object();
    private static object lock2 = new object();

    public static void main(string[] args) {
        new thread(() -> {
            synchronized (lock1) {
                system.out.println("thread1 acquired lock1");
                try {
                    thread.sleep(1000);
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
                system.out.println("thread1 try to acquire lock2");
                synchronized (lock2) {
                    system.out.println("thread1 acquired lock2");
                }
            }
        }, "t1").start();

        new thread(() -> {
            synchronized (lock2) {
                system.out.println("thread2 acquired lock2");
                try {
                    thread.sleep(1000);
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
                system.out.println("thread2 try to acquire lock1");
                synchronized (lock1) {
                    system.out.println("thread2 acquired lock1");
                }
            }
        }, "t2").start();
        
        // 检测死锁
        checkdeadlock();
        system.out.println("main thread end");
    }

    public static void checkdeadlock() {
        threadmxbean mxbean = managementfactory.getthreadmxbean();
        scheduledexecutorservice scheduled = executors.newscheduledthreadpool(1);
        // 初始等待5秒,每隔10秒检测一次
        scheduled.scheduleatfixedrate(()->{
            long[] threadids = mxbean.finddeadlockedthreads();
            if (threadids != null) {
                system.out.println("检测到死锁线程:");
                threadinfo[] threadinfos = mxbean.getthreadinfo(threadids);
                for (threadinfo info : threadinfos) {
                    system.out.println(info.getthreadid() + ":" + info.getthreadname());
                }
            }
        }, 5l, 10l, timeunit.seconds);
    }
}

    上面这段程序在99.99%的情况下都会发生死锁,但是从理论的角度来讲,死锁并不是100%会发生的,比如:线程t1先启动并获取了锁lock1,在休眠的这1s的过程中,jvm并未发生线程调度(实际上基本不可能),t2未得到执行也未获取到锁lock2,这时候t1休眠结束并获取了锁lock2,那么这种情况下就不会发生死锁了。
    如何写一个100%会发生死锁的程序呢?直接上代码:

public class deadlocktest {

    private static object lock1 = new object();
    private static object lock2 = new object();

    private static volatile boolean flag1 = false;
    private static volatile boolean flag2 = false;

    public static void main(string[] args) {
        new thread(() -> {
            synchronized (lock1) {
                flag1 = true;
                system.out.println("thread1 acquired lock1");
                while (!flag2) {
                    // 无限循环,等待thread2获取到lock2后再继续往下执行(相比使用thread.sleep(1000)在理论上是100%会出现死锁)
                    thread.yield();
                }
                system.out.println("thread1 try to acquire lock2");
                synchronized (lock2) {
                    system.out.println("thread1 acquired lock2");
                }
            }
        }, "t1").start();

        new thread(() -> {
            synchronized (lock2) {
                flag2 = true;
                system.out.println("thread2 acquired lock2");
                while (!flag1) {
                    thread.yield();
                }
                system.out.println("thread2 try to acquire lock1");
                synchronized (lock1) {
                    system.out.println("thread2 acquired lock1");
                }
            }
        }, "t2").start();

        // 检测死锁
        checkdeadlock();
        system.out.println("main thread end");
    }

    public static void checkdeadlock() {
        threadmxbean mxbean = managementfactory.getthreadmxbean();
        scheduledexecutorservice scheduled = executors.newscheduledthreadpool(1);
        // 初始等待5秒,每隔10秒检测一次
        scheduled.scheduleatfixedrate(() -> {
            long[] threadids = mxbean.finddeadlockedthreads();
            if (threadids != null) {
                system.out.println("检测到死锁线程:");
                threadinfo[] threadinfos = mxbean.getthreadinfo(threadids);
                for (threadinfo info : threadinfos) {
                    system.out.println(info.getthreadid() + ":" + info.getthreadname());
                }
            }
        }, 5l, 10l, timeunit.seconds);
    }
}