2014-05-24 70 views
1

假設我有一個名爲線程myList的數組列表,所有這些都是使用實現Runnable接口的類myRunnable的一個實例創建的,也就是說,線程共享相同的代碼以在myRunnable的run()方法中執行。現在假設我有另一個叫做singleThread的單線程,它是使用實現Runnable接口的類otherRunnable的一個實例創建的。如何在Java中爲一個線程同步一組多線程

我必須爲這些線程解決的同步挑戰如下:我需要myList中的所有線程來執行它們的代碼,直到某個點。一旦達到這一點,他們就睡着了。一旦myList中的所有線程都處於睡眠狀態,則應該喚醒singleThread(singleThread已經睡着)。然後singleThread執行它自己的東西,當它完成時,它應該休眠並且myList中的所有線程都應該被喚醒。想象一下,這些代碼被封裝在(true)的時候,所以這個過程必須一次又一次地發生。

這裏是我剛剛描述包括解決同步問題的嘗試的情況爲例:

class myRunnable extends Runnable 
{ 
    public static final Object lock = new Object(); 
    static int count = 0; 

    @override 
    run() 
    { 
    while(true) 
    { 
     //do stuff 
     barrier(); 
     //do stuff 
    } 
    } 

    void barrier() 
    { 
    try { 
     synchronized(lock) { 
      count++; 
      if (count == Program.myList.size()) { 
      count = 0; 
      synchronized(otherRunnable.lock) {   
       otherRunnable.lock.notify(); 
      } 
      } 
      lock.wait(); 
     } 
    } catch (InterruptedException ex) {} 
    } 
} 

class otherRunnable extend Runnable 
{ 
    public static final Object lock = new Object(); 

    @override 
    run() 
    { 
    while(true) 
    { 
     try { 
     synchronized(lock) { 
      lock.wait(); 
     } catch (InterruptedException ex) {} 

     // do stuff 

     try { 
     synchronized(myRunnable.lock) { 
      myRunnable.notifyAll(); 
     } 
    } 
    }   
} 

class Program 
{ 
    public static ArrayList<Thread> myList; 

    public static void main (string[] args) 
    { 
    myList = new ArrayList<Thread>(); 

    for(int i = 0; i < 10; i++) 
    { 
     myList.add(new Thread(new myRunnable())); 
     myList.get(i).start(); 
    } 
    new Thread(new OtherRunnable()).start(); 
    } 
} 

基本上我的想法是使用一個計數器,以確保在myList中的線程只是等待,除了遞增計數器的最後一個線程將計數器重置爲0,通過通知其鎖定來喚醒singleThread,然後通過等待myRunnable.lock最後一個線程進入休眠狀態。在一個更抽象的層次上,我的做法是使用某種阻隔在myList中的線程停止其執行在一個臨界點,那麼最後一個線程擊中屏障醒來singleThread並進入睡眠,以及,然後singleThread使得它的東西當它完成後,它將屏障中的所有線程喚醒,以便它們可以再次繼續。

我的問題是,我的邏輯存在缺陷(可能還有更多)。當最後一個線程擊中屏障通知otherRunnable.lock,有一個機會,立即上下文切換可能發生,這給CPU到singleThread,前最後一個線程可以在myRunnable.lock執行的等待(和睡覺)。然後singleThread將執行所有的東西,將在myRunnable.lock執行notifyAll的,並在myList中的所有線程將被喚醒,除了最後一個線程打擊,因爲它尚未執行的等待命令的屏障。然後,所有的線程會再次這樣做自己的東西,並會再次命中障礙,但數量將永遠等於myList.size(),因爲前面提到的最後一個線程最終會被再次安排和將執行的等待。單線程反過來也會在第一行執行等待,結果導致我們陷入僵局,每個人都在睡覺。

所以我的問題是:什麼將這些線程,以達到在某種程度上安全死鎖的同一時間之前,但在描述所期望的行爲同步的好辦法?

+0

tl; dr您可以對您嘗試解決的問題提供更高級的描述嗎? - 在'java.util.concurrent'中可能有一個很好的解決方案。 –

+0

也許我的問題還不清楚,所以我會嘗試以更簡單的方式再次解釋它。我有n個線程和另一個線程x。我想知道一種實現以下行爲的方法:首先執行n個線程,直到出現嚴重代碼行,然後停止執行並進入休眠狀態,然後喚醒x並開始執行,然後x喚醒n個線程並轉至睡眠,然後n個線程再次執行,直到臨界代碼行,停止執行,進入睡眠狀態,再次喚醒x,依此類推。在符號中:n - > x - > n - > x .... –

+0

您可能採取的一種方法是在調用notify all之前讓'OtherRunnable'檢查所有其他線程的狀態。如果'OtherRunnable'看到'MyRunnable'的任何線程都沒有處於等待狀態,則再次進入休眠狀態,直到所有MyRunnable線程處於等待狀態。請參閱[Thread.State](http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html)。 (我沒有發佈這個答案,因爲它對我來說似乎相當不利,但我認爲它應該完成這項工作。) – Tyler

回答

3

根據您的評論,聽起來像一個CyclicBarrier將完全符合您的需求。從文檔(重點煤礦):

一個同步輔助該允許所有一組線程等待彼此達成共同屏障點。 CyclicBarriers在涉及固定大小的線程的程序中很有用,它必須偶爾等待對方。由於這個屏障被稱爲循環,所以在等待線程被釋放後可以重新使用

不幸的是,我自己並沒有使用它們,所以我不能給你具體的指示。我認爲基本的想法是用barrierAction這個雙參數的構造函數構造你的障礙。在完成此任務後,在此屏障上有n線程await(),之後barrierAction被執行,之後n線程將繼續。

從Javadoc文檔CyclicBarrier#await()

如果當前線程是最後一個線程到達,並在構造方法中提供一個非空的屏障作用,那麼當前線程允許之前運行的動作其他線程繼續。如果在屏障動作期間發生異常,那麼該異常將在當前線程中傳播並且屏障被置於斷開狀態。