• 并发编程:一个100%会发生死锁的程序


        多线程是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();
        // 这里的flag需要用volatile修饰,以保证线程间的可见性
        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);
        }
    }
    
  • 相关阅读:
    JUnit快速入门
    CoreJava笔记之线程
    CoreJava笔记之JavaBean、静态方法static和final
    CoreJava基础之构造器
    JAVA环境配置
    软件测试工程师应该具备的能力
    Apache和Tomcat的整合过程(转载)
    ios界面适配和图标的各种大小
    iPhone6和iPhone6 plus的iOS8设计尺寸参考指南
    博主写的非常详细的ios网络请求、uiwebview与js交互
  • 原文地址:https://www.cnblogs.com/ocean234/p/10690962.html
Copyright © 2020-2023  润新知