2017-04-14 4 views
1

同期ブロックを理解しようとしています。ここでは、1つのプロデューサスレッドと2つのコンシューマスレッドを実装しました。Javaマルチスレッド:遅いプロデューサと高速コンシューマ

LinkedListが空であるためスレッドで例外が発生し続けます。

package com.Main; 

import com.qed.Consumer; 
import com.qed.Producer; 
import com.qed.Store; 

public class Main { 

public static void main(String[] args) throws InterruptedException { 
    Store st = new Store(); 

    Thread populate = new Thread(new Producer(st)); 
    Thread con1 = new Thread(new Consumer(st)); 
    Thread con2 = new Thread(new Consumer(st)); 
    con1.setName("A"); 
    con2.setName("B"); 
    populate.start(); 
    con1.start(); 
    con2.start(); 

    populate.join(); 
    con1.join(); 
    con2.join(); 
    if(populate.isAlive()){ 
     con1.interrupt(); 
     con2.interrupt(); 
    } 
    } 
} 

package com.qed; 

import java.util.LinkedList; 

public class Store { 

private LinkedList<Integer> qu = new LinkedList<Integer>(); 
private final Object lock = new Object(); 

public void add(int data){ 
    try{ 
     while(qu.size() ==10){ 
      Thread.sleep(1); 
      } 
     qu.add(data); 
    }catch(InterruptedException ie){ 
     ie.printStackTrace(); 
    } 
} 

public int remove(){ 
    int data=0; 
    try{ 
     synchronized(lock){ 
      while(qu.size() == 0){ 
       Thread.sleep(1); 
       } 
      data = qu.removeFirst(); 
     } 
    }catch(InterruptedException ie){ 
     ie.printStackTrace(); 
    } 
    return data; 
    } 
} 

package com.qed; 

public class Consumer implements Runnable{ 

private Store st; 
public Consumer(Store st){ 
    this.st=st; 
} 


public void run(){ 
    while(true){ 
     System.out.println(Thread.currentThread().getName() + ". " +st.remove()); 
    } 
    } 
} 

package com.qed; 

public class Producer implements Runnable{ 

private Store st; 
private final int runs = 5000; 
public Producer(Store st){ 
    this.st = st; 
} 

public void run(){ 
    int data = 0; 
    int curRun =0; 
    while(++curRun < runs){ 
     st.add(data+=200); 
     } 
    System.out.println("DONE."); 
    } 
} 

スタックトレース:

Exception in thread "B" Exception in thread "A" 
java.util.NoSuchElementException  
    at java.util.LinkedList.removeFirst(Unknown Source)  
    at com.qed.Store.remove(Store.java:46)  
    at com.qed.Consumer.run(Consumer.java:20)  
    at java.lang.Thread.run(Unknown Source)  
java.util.NoSuchElementException  
    at java.util.LinkedList.removeFirst(Unknown Source)  
    at com.qed.Store.remove(Store.java:46)  
    at com.qed.Consumer.run(Consumer.java:20)  
    at java.lang.Thread.run(Unknown Source)  
+2

'add()'で同期しないと、未定義の動作です。 – 1000ml

答えて

1

あなたにも追加することにロックする必要があります。あなたのコードでは、消費者がエントリを削除したいかもしれない間に、プロデューサはキューを更新することができます!

2つのスレッドが同じキューを並列に変更すると、すべてのベットがオフになります。

このロックを使用すると、複数のコンシューマーが互いに踏み合うことができなくなります。

したがって、値を追加するセクションに同じ種類のロックを追加します。

さらに、EJPは正しいです。本当の解決方法は、wait()やnotify()などの低レベルのシグナリングメソッドを利用することです。もちろん、これらを使用すると、非常に異なる動作につながります。

あなたのコメントがあります:これらは2つの異なることであることに留意してください。A)消費者/プロデューサが互いに信号を送信するB)消費者/同じ外観で同期を生成する。

私はあなたが「A)」を望んでいないことを理解していますが、には「B)」が必要です。そうしないと、キューが破損し、驚きが発生します。

+0

正しいですが、彼はまたスリープする代わりに 'wait()'する必要があります。また、追加や削除の際に 'notify()'する必要があります。 – EJP

+0

はい、いいえ。私は彼がここで実験していると思う。シグナリングを使用することは、見るべき自然な拡張です。しかし、私はそれに応じて答えを更新しました。ありがとう。 – GhostCat

+0

@GhostCat正しいですよ。私はここで実験しています。私は単一のプロデューサスレッドがキューがいっぱいになるまで追加を続けることができるキューを実装しようとしています。コンシューマは、プロデューサとコンシューマが互いに同期されていないメカニズムをここで実装しようとしています。待機メソッドを使用して通知メソッドを使用する必要がないため、コンシューマが使用するロックと同じロックで同期メソッドを使用する必要があります。これは、消費者がremoveメソッドにアクセスしている間、プロデューサがロックアウトされるという私の目的に反するものです。 – Manik

0

)メソッドをここに呼び出す必要があります。
wait()は、他のスレッド呼び出しがウェイクアップを知らせるまでスレッドを待機させます。
sleep()次の文を指定した時間だけ実行しないでください。
プログラムスニペットが表示されている場合は、オブジェクトを使用してモニタの可用性をチェックする同期ブロックを使用しています。しかし、オブジェクト監視メソッドwait/notify/notifyAllを使用していないので、これらのメソッドを呼び出さずにロックを取得して解放しようとしています。コンシューマとプロデューサの両方で使用されるオブジェクトをリストするので、このリストオブジェクトモニタを使用してすべてのスレッドを同期させる必要があります。あるスレッドがそのモニターを取得した場合、他のスレッドはそれにアクセスできなくなります。すべてのオブジェクトにはモニタが1つしかないためです。このアプローチは、すべての作業スレッド間の同期の問題を解決します。

0

問題はStoreクラスの実装です。睡眠の代わりに、要素を追加したり削除したりするときにwait()notifyのメカニズムを実装する必要があります。

あなたはすべての消費者と生産者の間で単一Storeインスタンスを共有で正しいですが、お店がBlockingQueue

のように動作する必要がありますので、あなたが似て実装するJDKからBlockingQueueの既存の実装を使用するか、またはあなたのStoreクラスを変更のいずれか機構。

implement-your-own blocking queue in java

それが役に立てば幸い!

関連する問題