Java併發(九)Condition和執行緒順序執行
阿新 • • 發佈:2018-12-30
有的時候我們希望執行緒按照希望的順序依次執行,比如執行緒A,B,C,按照順序依次執行,這時候就要用到阻塞和喚醒,之前的時候我們學到過wait()
和nofity/notifyAll()
這兩個方法,這裡我們使用java.concurrent.locks.Lock
介面來實現類似的功能;
用到的包和類
java.concurrent.locks.Lock:介面
|-->java.concurrent.locks.ReentrantLock:實現類
|-->java.util.concurrent.locks.Condition:抽象類
方法:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
要求
- 建立一個TestAlternate類,有三個方法loopA(),loopB(),loopC(),分別列印A,B,C
- 主函式中建立三個執行緒,繫結三個匿名類實現Runnable介面
- 主函式中迴圈10次,使得每次列印都按照A–>B–>C的順序來列印
建立類
TestAlternate.java
class TestAlternate{
//執行緒執行順序標記,1:表示loopA執行,2:表示loopB執行,3:表示loopC執行
private volatile int number = 1;
//獲得lock鎖
private Lock lock = new ReentrantLock();
//建立三個condition物件用來await(阻塞)和signal(喚醒)指定的執行緒
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
protected void loopA(){
lock.lock();//上鎖
try {
/*如果不是第一個標誌位,就阻塞,為了解決虛假喚醒問題,使用while關鍵字
*/
while(number!=1){
try {
c1.await();//阻塞類似wait()
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-A");
number = 2;//使能第二個方法
c2.signal();//喚醒第二個執行緒,類似notify()方法
} finally {
lock.unlock();//解鎖
}
}
protected void loopB(){
lock.lock();//上鎖
try {
//如果不是第一個標誌位,就阻塞
while(number!=2){
try {
c2.await();//阻塞類似wait()
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-B");
number = 3;//使能第3個方法
c3.signal();//喚醒第三個執行緒,類似notify()方法
} finally {
lock.unlock();//解鎖
}
}
protected void loopC(){
lock.lock();//上鎖
try {
//如果不是第一個標誌位,就阻塞
while(number!=3){
try {
c3.await();//阻塞類似wait()
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-C");
number = 1;//使能第1個方法
c1.signal();//喚醒第一個執行緒,類似notify()方法
} finally {
lock.unlock();//解鎖
}
}
}
測試輸出:
loopA0-A
loopB0-B
loopC0-C
loopA1-A
loopB1-B
loopC1-C
loopC2-C//虛假喚醒問題
loopA2-A
loopB2-B
虛假喚醒的注意事項
出現虛假喚醒的原因:
假如A1,A2兩個執行緒爭奪loopA,A2奪得了cpu執行權,結果發現此時A2的標記為number不是1,於是await,A2開始阻塞這個時候釋放鎖和資源,然後B,C執行緒得到cpu執行權按照順序執行完畢,此時A的標誌位是1,此時A1和A2的鎖都是c2.await()A1,A2同時被被喚醒,A1搶到了cpu執行權,列印輸出loopA,並改變number為2,然後由於A2也被喚醒,但是由於是if
語句,在阻塞前只判斷了一次,即便此時number不是2了,但是A2不會再次判斷number的值,繼續往下執行,導致重複輸出loopA
。
解決方案:
把if
替換為while
,使得每次都判斷number的值是否正確,保證了程式的正常執行,避免虛假喚醒的情況出現。