並發編程:一個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);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM