題目描述
假設有這麼一個類:
class ZeroEvenOdd {
public ZeroEvenOdd(int n) { … } // 構造函數
public void zero(printNumber) { … } // 僅打印出 0
public void even(printNumber) { … } // 僅打印出 偶數
public void odd(printNumber) { … } // 僅打印出 奇數
}
相同的一個 ZeroEvenOdd 類實例將會傳遞給三個不同的線程:
線程 A 將調用 zero(),它只輸出 0 。
線程 B 將調用 even(),它只輸出偶數。
線程 C 將調用 odd(),它只輸出奇數。
每個線程都有一個 printNumber 方法來輸出一個整數。請修改給出的代碼以輸出整數序列 010203040506… ,其中序列的長度必須爲 2n。
示例 1:
輸入:n = 2
輸出:“0102”
說明:三條線程異步執行,其中一個調用 zero(),另一個線程調用 even(),最後一個線程調用odd()。正確的輸出爲 “0102”。
示例 2:
輸入:n = 5
輸出:“0102030405”
解題過程
這道題花的時間長了一些。首先最先想到的是嘗試用Condtion來解決,但是實際在處理過程中,發現必須要將特殊情況的場景全部判斷清楚,包括每個線程起始,和退出。這樣思路下寫出來的代碼,容易導致某些場景下最後線程阻塞,提交超時。
另外多線程環境,本地測試情況經常會覺得自己答案正確,實際確不是,這個時候不要懷疑係統驗證有問題,需要細心檢查自己的邏輯。
後面決定改變思路,第一點,每個線程只負責做自己的事情,比如第一個線程負責打印n個0,奇數線程負責打印n+1/2個奇數,偶數線程負責打印n/2個偶數,每個線程自己有個局部變量i進行循環打印。通過信號量控制每個線程按順序循環打印即可。
class ZeroEvenOdd {
private int n;
public ZeroEvenOdd(int n) {
this.n = n;
}
Semaphore zeros=new Semaphore(1);
Semaphore odds=new Semaphore(0);
Semaphore evens=new Semaphore(0);
//線程 A 將調用 zero(),它只輸出 0 。
//線程 B 將調用 even(),它只輸出偶數。
//線程 C 將調用 odd(),它只輸出奇數。
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero(IntConsumer printNumber) throws InterruptedException {
int i=0;
while(i<n){
zeros.acquire();
printNumber.accept(0);
if((i+2)%2==0){
odds.release();
}else{
evens.release();
}
i++;
}
}
//打印偶數
public void even(IntConsumer printNumber) throws InterruptedException {
int i=0;
while(i<n/2){
evens.acquire();
printNumber.accept(i*2+2);
i++;
zeros.release();
}
}
//打印奇數
public void odd(IntConsumer printNumber) throws InterruptedException {
int i=0;
while(i<(n+1)/2){
odds.acquire();
printNumber.accept(i*2+1);
i++;
zeros.release();
}
}
public static void main(String[] args) {
ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
new Thread(()->{
try {
zeroEvenOdd.zero(value -> {
System.out.print(value);
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
zeroEvenOdd.even(value -> {
System.out.print(value);
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
zeroEvenOdd.odd(value -> {
System.out.print(value);
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}