1. 程式人生 > >Java併發(九)Condition和執行緒順序執行

Java併發(九)Condition和執行緒順序執行

有的時候我們希望執行緒按照希望的順序依次執行,比如執行緒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();

要求

  1. 建立一個TestAlternate類,有三個方法loopA(),loopB(),loopC(),分別列印A,B,C
  2. 主函式中建立三個執行緒,繫結三個匿名類實現Runnable介面
  3. 主函式中迴圈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

虛假喚醒的注意事項

出現虛假喚醒的原因
假如A1A2兩個執行緒爭奪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的值是否正確,保證了程式的正常執行,避免虛假喚醒的情況出現。