天天看點

一道很有意思的java線程題

這幾天看結城浩的《java多線程設計模式》,跟着做一些習題,有幾道題目很有意思,記錄下自己的體會。

  首先是題目(在原書212頁,書尾有解答):

public class Main {
    public static void main(String[] args) {
        try {
            Blackhole.enter(new Object());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}      
public class Blackhole {    
        public static void enter(Object obj) throws InterruptedException {
        System.out.println("1");
        magic(obj);
        System.out.println("2");
        synchronized (obj) {
            System.out.println("3");
        }
    }

    private static void magic(Object obj){}
}      

 代碼很簡單,要求寫出magic()代碼,使得輸出是

1
2      

不能出現3.

思路:很明顯要想不輸出3,magic()必須得到obj鎖,且不能釋放才行。

private static void magic(final Object obj) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                synchronized (obj) {
                    synchronized (this) {
                        this.setName("LockNow");
                        this.notifyAll();
                    }
                    while (true) {
                    }
                }
            }
        };
        synchronized (thread) {
            thread.setName("");
            thread.start();
            while (thread.getName().equals("")) {
                thread.wait();
            }
        }
    }      

 作者的思路很巧妙,通過thread.name的值來處理2個線程的執行次序。

   1. 建立一個内部Thread執行個體thread,先不start()

   2. 然後由主線程獲得thread鎖,并啟動thread線程,然後開始等待。

   3. thread線程會去獲得obj鎖,獲得obj鎖之後,該線程會修改自己name,并通知主線程。

   4. 主線程發現條件滿足,繼續執行

   剛看到答案,有個很大疑問,主線程獲得thread鎖之後,啟動thread線程,而thread線程為了修改name,必須獲得自己的鎖(否則運作時會報錯java.lang.IllegalMonitorStateException),這不死鎖了嗎?

   仔細一想又不會,因為新線程開啟之後,如果新線程運作到synchronized (this)被阻擋而無法修改name,主線程肯定會進入wait,而wait時主線程釋放thread鎖,新線程就可繼續往下跑。

  短短幾行代碼,線程之間同步協調環環相扣,不得不佩服作者的功力!