2017-11-15 8 views
1

私を助けてくれますか?スレッドが特定のイベントが発生するのを待つ方法

私がしようとしているのは、プリンタの例です。より多くのプリンタと印刷待ちのドキュメントがあります。

Printerは、Threadです。ドキュメントが到着するまでスリープしてから印刷し、再びスリープ状態になります。

PrinterManagerもスレッドです。キューから文書を収集し、無料のPrinterに送信します。無料のプリンタを見つけるためにSemaphoreを使用しています。

問題は、wait-notifyペアの周りです。プリンターは、マネージャーが文書を送信するまで待つ必要があります。その後、1秒待って「プリント」します。ロックオブジェクトとしては、stickを使用します。

何らかの理由で、機能しません。ドキュメントはプリンタに正常に送信されますが、プリンタは起動されません。なぜ、助けてくれますか?

プリンタのスレッド:

public class Printer extends Thread { 

    private final Semaphore semaphore; 
    private final Object stick; 
    private String document; 

    public Printer(Semaphore semaphore, Object stick) { 
     this.semaphore = semaphore; 
     this.stick = stick; 
    } 

    public void setDocument(String document) { 
     this.document = document; 
    } 

    @Override 
    public void run() { 
     while (true) { 
      try { 
       stick.wait(); 
       Thread.sleep(1000); 
       System.out.println("Printing: " + document); 
       semaphore.release(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

マネージャスレッド:(?あなたが実験を行います)

public class PrinterManager extends Thread { 

    private final Printer[] printers = new Printer[1]; 
    private final Object stick = new Object(); 
    private final Semaphore semaphore = new Semaphore(printers.length); 
    private final DocumentQueue queue; 

    public PrinterManager(DocumentQueue queue) { 
     this.queue = queue; 
     printers[0] = new Printer(semaphore, stick); 
    } 

    @Override 
    public void run() { 
     while (true) { 
      try { 
       semaphore.acquire(); 
       String toPrint = takeNextDocument(); 
       printers[0].setDocument(toPrint); 
       synchronized (stick) { 
        stick.notify(); 
       } 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    private String takeNextDocument() throws InterruptedException { 
     return queue.take(); 
    } 
} 
+0

スティックではなくプリンタに通知してください。 – 5tingr4y

+1

いいえ、それは 'IllegalMonitorStateException'を引き起こします。 –

+1

正しくリコールすれば、' stick'でも 'wait()'コールを同期させる必要があります –

答えて

1

は、私が1000年の何倍文書を印刷するプリンタを実行する方法のだろうか。

しかし、最初にstick.wait()をsynchronizedブロック内のPrinterの実行メソッドに入れるか、そこにIllegalMonitorStateException例外が発生します。

競合状態とデッドロックと呼ばれる並行性の問題に直面しました。結果は、コードが実行される順序に依存することを意味します。時々あなたは幸運かもしれないし、プログラムはあなたが期待どおりに動作するでしょう。しかし、他の時代には、デッドロック(別の並行処理の問題)があります。

命令がその順序で実行されていると想像:

  1. PrinterManager.run()

  2. PrinterManager.run()実行がスティックに

  3. Printer.run()実行を通知し、プリンタへのセマフォ

  4. PrinterManager.run()セットの文書を取得stick.wait()

...これはデッドロックを取得する方法です。 PrinterManagerは、Printerが届く前にstickに通知しました。その後、PrinterManagerはセマフォの最後の許可を取得し、Printerがリリースされるまで待っていました(これは決して待たずに待っています)。両方のオブジェクトがお互いに待って始め、両方がブロックされました。

は、通知されたオブジェクトが予期したとおりに動作するよりも、スティックで通知されるまでには、まで待つことができました。だからこそあなたは競争状態にあり、何が起こるかは決して予測できず、これは非常に悪いことです。

シンプルな解決方法(ベストはありませんが、素早く、あまり知られていない追加のテクニックはありません)では、スティックを取り除くことがあります。代わりに、二つのカウンタを導入:

numberOfFinishedPrintTasksPrinterが各ターンの後に、それをインクリメントします)

lastSubmittedPrintTaskNumberPrinterManagerがそれぞれ提出後numberOfFinishedPrintTasks + 1にそれを設定してください)。

PrinterManager条件を確認する必要があります。numberOfFinishedPrintTasks == lastSubmittedPrintTaskNumberプリンタがすべての作業を完了し、新しいタスクを送信できることを意味します。

P.S. Threadクラスを継承しないでください。 Threadに新しい機能を追加しないでください。特定のジョブを実行するだけです。 そのためには、Runnableインターフェイスを実装するオブジェクトを作成し、そのオブジェクトを新しいThreadインスタンスに渡す必要があります。

関連する問題