這幾天看結城浩的《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鎖,新線程就可繼續往下跑。
短短幾行代碼,線程之間同步協調環環相扣,不得不佩服作者的功力!