2017-07-31 1 views
3

私はJavaの同期とスレッド間の通信を理解するためにPCの問題に取り組んできました。一番下にあるコードを使用して、出力がJava同期を使用してプロデューサ/コンシューマを理解する

Producer produced-0 
Producer produced-1 
Producer produced-2 
Consumer consumed-0 
Consumer consumed-1 
Consumer consumed-2 
Producer produced-3 
Producer produced-4 
Producer produced-5 
Consumer consumed-3 
Consumer consumed-4 

だったが、出力は私の理解は、消費者が通知されているので、私はこのような出力を期待

Producer produced-0 
Consumer consumed-0 
Producer produced-1 
Consumer consumed-1 
Producer produced-2 
Consumer consumed-2 
Producer produced-3 

として以下のようなものであってはなりませんメソッドが終了すると、プロダクションメソッドがロックを解除するとすぐに生成された値。その結果、待機していた消費者ブロックは、生成された値を消費するために同期状態取得ロックに入り、一方、プロデューサメソッドはブロックされる。このロックは、同期のためにブロックされたプロデューサスレッドによって獲得された消費メソッドの最後に解放され、ロックは取得されたために各メソッドがブロックされるとサイクルが継続する。

私は何を誤解したか教えてください。通知& &のThread.sleep

Object.notify():2 mothodsのおかげ

package MultiThreading; 

//Java program to implement solution of producer 
//consumer problem. 
import java.util.LinkedList; 

public class PCExample2 
{ 
public static void main(String[] args) 
        throws InterruptedException 
{ 
    // Object of a class that has both produce() 
    // and consume() methods 
    final PC pc = new PC(); 

    // Create producer thread 
    Thread t1 = new Thread(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      try 
      { 
       while (true) { 
        pc.produce(); 
       }     
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    // Create consumer thread 
    Thread t2 = new Thread(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      try 
      { 
       while (true) { 
        pc.consume(); 
       } 
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    // Start both threads 
    t1.start(); 
    t2.start(); 

    // t1 finishes before t2 
    t1.join(); 
    t2.join(); 
} 

// This class has a list, producer (adds items to list 
// and consumber (removes items). 
public static class PC 
{ 
    // Create a list shared by producer and consumer 
    // Size of list is 2. 
    LinkedList<Integer> list = new LinkedList<>(); 
    int capacity = 12; 
    int value = 0; 

    // Function called by producer thread 
    public void produce() throws InterruptedException 
    {   
     synchronized (this) 
     { 
      // producer thread waits while list 
      // is full 
      while (list.size()==capacity) 
       wait(); 

      System.out.println("Producer produced-" 
              + value); 

      // to insert the jobs in the list 
      list.add(value++); 

      // notifies the consumer thread that 
      // now it can start consuming 
      notify(); 

      // makes the working of program easier 
      // to understand 
      Thread.sleep(1000); 
     } 
    } 

    // Function called by consumer thread 
    public void consume() throws InterruptedException 
    { 
     synchronized (this) 
     { 
      // consumer thread waits while list 
      // is empty 
      while (list.size()==0) 
       wait(); 

      //to retrive the ifrst job in the list 
      int val = list.removeFirst(); 


      System.out.println("Consumer consumed-" 
              + val); 

      // Wake up producer thread 
      notify(); 

      // and sleep 
      Thread.sleep(1000); 
     } 
    } 
} 
} 
+0

https://stackoverflow.com/questions/2332537/producer-consumer-threads-using-a-queue/37767243#37767243 –

+0

これらのスレッドを移動するとどうなるかを知りたいかもしれません。 sleep(1000) 'は' synchronized'ブロックを呼び出します。 –

答えて

0

現在取り込まれているロック(スレッドAと呼ぶ)を呼び出す最初のスレッドは、ロックの現在のオーナースレッドがそれを放棄するとすぐにロックを取得することはありません。また、スレッドAがスレッドAを取得しようとしたためにロックを要求しました。順序付けられた「キュー」はありません。 hereおよびhereを参照してください。したがって、プロデューサスレッドのwhileループが繰り返され、プロデューサスレッドが別のロックを作成する前に、プロデューサの出力によって判断すると、プロデューサがロックを解除した後、消費者がロックを取得するのに十分な時間がないように見えます(他の回答が指摘したように、Thread.sleep()はスリープスレッドにロックを解放させません)、コンシューマが不運な場合、プロデューサはコンシューマが最初にそこにいたとしてもロックを再取得します。

しかし、別の誤解があるようです。プロデューサスレッドは、リストに12個の要素が含まれるまでPCで決して「待機」しないので、プロデューサが少なくとも12個の要素を生成したときにのみ消費者スレッドにロックが保証されます(これは、プログラムはプロデューサスレッドがPC上でwait()を呼び出すまでチャンスを得ることはありませんが、リスト全体を消費します。これは、消費者のターンであり、リストに12個未満の要素が含まれている場合、が通知されるのを待っていないので、通知されませんが、がブロックされてのみとなりました。 PCのロックを「予期する」か「期待する」(「待機」と「ブロック」の違いについてはhereも参照)。ので、あなたは、それによって消費者のスレッド(うまくいけば、あなたはこれに頼るべきではありません)ロックを取得するのに十分な時間を与え、同期ブロック外の2つのThread.sleep()呼び出しを入れていても、消費者のスレッドからの呼び出しnotify()は効果がありませんプロデューサスレッドは決して待機状態にはなりません。

本当に両方のスレッドがPCを交互に変更するようにするには、リストに12(または多くの)要素が含まれている場合とは対照的に、リストサイズがゼロより大きい場合のみプロデューサスレッドを待機させる必要があります。

+0

スレッドの優先順位を使用してスレッドの実行順序を固定することはできませんでしたか? 2つのスレッドA(1)とB(5)がブロックされている場合、JVMがリソースをB(5)に優先して優先順位を高くしないでください。上記のケースで消費者スレッドが優先順位が高い場合はどうなりますか?私はしようとしたが、私はまだ同じ順序を参照してください。 – Britto

+0

@Britto私はこの問題の専門家ではなく、私はその質問の答えを知らないので、あなたはどこか他の人に尋ねたり、stackoverflowで検索しなければならないかもしれません。 – Stingy

0

ご注意

は、このオブジェクトのモニターで待機中のスレッドを1つ再開します。このオブジェクトを待機しているスレッドがあれば、そのうちの1つが起動されるように選択されます。選択は任意であり、実装の裁量で発生します。スレッドは、waitメソッドの1つを呼び出して、オブジェクトのモニタを待機します。
現在のスレッドがこのオブジェクトのロックを放棄するまで、起動されたスレッドは続行できません。awakenedスレッドは、このオブジェクトで同期するために積極的に競合している他のスレッドと通常の方法で競合します。;例えば、目覚めたスレッドは、このオブジェクトをロックする次のスレッドであるという信頼できる特権も不利益もない。

のThread.sleep():

は、精度と正確さを条件として、指定されたミリ秒数を加えナノ秒の指定された数のために(一時的に実行を停止)スリープ状態に現在実行中のスレッドを引き起こしますシステムタイマーおよびスケジューラー。 スレッドは、モニタの所有権を失うことはありません。

OK。今通知しても、このオブジェクトも監視するスレッドを起動しますが、目覚めたスレッドはこのオブジェクトで同期するために競合します。あなたのプロデューサーが消費者に通知してロックを解除すると、プロデューサーと消費者は同じポイントに立って競争します。そして、Thread.sleepはあなたが望む作業をしません。ドックが言ったようにスリープするとロックを解除しません。だからこれは起こるかもしれない。

結論として、Thread.sleepは同期してもあまりよくありません。これを削除しても、通知の仕組みのために最初の出力が発生します。

@Andrew Sの回答がうまくいきます。APIから

0

:目覚めのスレッドが積極的にこのオブジェクトを同期させるために、競合する可能性のある他のスレッドと通常の方法で競争します。例えば、目覚めたスレッドは、このオブジェクトをロックする次のスレッドであるという信頼できる特権も不利益もない。他のスレッドにロックを取得する利点を与える同期ブロック外

移動sleep()

関連する問題