2017-11-01 12 views
1

リエントラントロックを使用して典型的なプロデューサコンシューマを実装しようとしています。 プロデューサスレッドは偶数を出力し、コンシューマスレッドは奇数を出力します。共有バッファプロデューサのコンシューマがデッドロック(またはライブロック)するのはなぜですか?

public class SharedBuffer { 
    Lock lock = new ReentrantLock(); 

    Condition evenNotPrinted = lock.newCondition(); 
    Condition oddNotPrinted = lock.newCondition(); 

    int currentNumber = 0; 

    public void printEven() { 
     System.out.println("from even"); 
     try { 
      lock.lock(); 
      try { 
       oddNotPrinted.await(); 
      } 
      catch(InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println(" being printed by thread " + "" + Thread.currentThread().getName() + " "+ currentNumber); 
      currentNumber++; 
      evenNotPrinted.signalAll(); 
     } 
     finally { 
      lock.unlock(); 
     } 
    } 

    public void printOdd() { 
     System.out.println("from odd"); 
     try { 
      lock.lock(); 
      try { 
       evenNotPrinted.await(); 
      } 
      catch(InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println(" being printed by thread " + "" + Thread.currentThread().getName() + " "+ currentNumber); 
      currentNumber++; 
      oddNotPrinted.signalAll(); 
     } 
     finally { 
      lock.unlock(); 
     } 
    } 
} 

public class OddEvenDriver { 
    public static void main(String[] args) { 

     //using runnables with lock buffer 

     SharedBuffer buf1 = new SharedBuffer(); 
     EvenPrinterRunnable epr = new EvenPrinterRunnable(buf1); 
     OddPrinterRunnable opr = new OddPrinterRunnable(buf1); 

     ExecutorService es = Executors.newFixedThreadPool(2); 
     es.submit(opr); 
     es.submit(epr); 
     es.shutdown(); 
    } 
} 

それは

を出力しているドライバクラス は、ここに私のコードですが、いくつかの理由では 実行可能なタスク

public class EvenPrinterRunnable implements Runnable { 
    SharedBuffer buf; 
    public EvenPrinterRunnable(SharedBuffer buf) { 
     this.buf = buf; 

     Thread.currentThread().setName("Even Runnable"); 
    } 

    @Override 
    public void run() { 

     for(int i = 0; i < 10; i++) { 
      buf.printEven(); 
     } 

    } 
} 

public class OddPrinterRunnable implements Runnable { 
    SharedBuffer buf; 
    public OddPrinterRunnable(SharedBuffer buf){ 
     this.buf = buf; 
     Thread.currentThread().setName("ODD Runnable"); 
    } 

    @Override 
    public void run(){ 
     for(int i = 0; i < 10; i++){ 
      buf.printOdd(); 
     } 
    } 
} 

がデッドロックされます

from even 
from odd 

各スレッドがロックを取得しているし、それが条件evenNotPrintedとoddNotPrintedに待機し、信号が呼び出されるまで、それらの誰もが進行しないことができるので、だから私の質問はある意味

from odd 
from even 

、ものと私はメソッド自体の開始時に各条件に合図しますか? 私はここで紛失しています

+1

'signal()'が呼ばれた後に 'await()'に達する可能性はありますか? – daniu

+0

これは合理的には明らかです:各スレッドはロックを獲得しようとしていて、もう一方がロックを取得してシグナルを受信した後に*送信するシグナルを待っています。最初のスレッドはロックを取得し、その信号を待ちます。 2番目のスレッドはロックを取得できません。だからデッドロックです。 –

+0

はい、ありがとうございました、私はそれが問題であることを理解しましたが、私はこのイディオムが頻繁に使用されるのを見ました。私は何が欠けていたのだろうと思っていた。 – TruckDriver

答えて

2

私の質問は、メソッド自体の開始時にそれぞれの条件を伝えますか?動作しません

号。条件変数は、スレッドがcondition.await()を呼び出すと以前に通知されたことを記憶していません。 condition.signal()condition.signalAll()関数は、他のスレッドがすでにシグナルを待っていない限り、何もしません。

状態変数はキューおよびセマフォや他のより高いレベルの同期オブジェクトを実装するために非常に特定の方法で使用されることが意図されている低レベルの同期メカニズムです。 Guarded Blocks tutorialに詳しく説明されています。 (注意:LockConditionオブジェクトに直接object.wait()object.notify()と​​ブロックに関するチュートリアル会談、その概念すべてのマップを)

あなたの基本的な問題は、あなたの2つのスレッドが互いに完全に対称的でないということです。それらのうちの1つは最初にに行かなければなりません。あなたのmain()スレッドは、それらの1つを目覚めさせなければならないか、 "あなたが最初に"と言う引数を使ってスレッドを構築する必要があります。

関連する問題