2016-04-28 8 views
0

私はこのような何かをしようとしていますが期待通りに動作しませんnのそして、2つのクラスProducerConsumerがあります。 Producerクラスにはputを呼び出すスレッドがあり、consumerクラスにはgetを呼び出すスレッドがあります。同期とのスレッドが

public class Q { 

    int n; 

    public void put(int n){ 
     System.out.println("Put " + n); 
     this.n = n; 
    } 

    public int get(){ 
     System.out.println("Got " + n); 
     return n; 
    } 
} 

LockClass:

public class LockClass { 

     private static Object Lock = new Object(); 

     private LockClass(){ 

     } 


     public static Object getLock(){ 
      return Lock; 
     } 
    } 

消費者:

私は LockClass.

だから、シングルトンクラスの唯一のインスタンスであるObject lockを使用して、それを同期しようとしています、ここに私のクラスQであります

public class Consumer implements Runnable { 

    Thread t; 
    Q q; 


    public Consumer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 

      synchronized(LockClass.getLock()){ 
       q.get(); 
      } 
      try { 
       System.out.println("Consumer slept"); 
       Thread.sleep(1000); 
       System.out.println("Consumer awake"); 

      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

生産者:

public class Producer implements Runnable { 

    Q q; 
    Thread t; 
    int i; 

    public Producer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
     i = 0; 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 
      i++; 
      synchronized(LockClass.getLock()){ 
       q.put(i); 
      } 
      try { 
       System.out.println("Producer slept"); 
       Thread.sleep(1000); 
       System.out.println("Producer awake"); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 


} 

DemoClass:だから

Put 1 
Producer slept 
Got 1 
Consumer slept 
Consumer awake 
Producer awake 
Got 1 
Consumer slept 
Put 2 
Producer slept 
Consumer awake 
Producer awake 
Got 2 
Consumer slept 
Put 3 
Producer slept 
Consumer awake 
Producer awake 
Got 3 
Consumer slept 
Put 4 
Producer slept 

、私は実際に作っています:このクラスは、私はこの上記のコードを実行すると、私はこのような何かを得る、

public class DemoClass { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     Q q = new Q(); 

     Producer prod = new Producer(q); 
     Consumer cons = new Consumer(q); 
    } 

} 

だから、主な機能を持っていますスレッドがプロデューサとコンシューマの両方でスリープして、コンテキストの切り替えに十分な時間があるようにします。今、両方が同じ時間に眠る必要がある場合、プロデューサーが最初に眠ったら、まず目を覚ますべきです。しかし、私が出力で見ることができるように、プロデューサは最初にスリープしますが、消費者はまだ目を覚ましています。最初に目を覚まされたスレッドがロックを取得し、ロックが解放されるまで別のスレッドが待機する必要があります。

私が期待しているように動作しないのはなぜですか?何か不足していますか?

+1

を指定するために、あなたが考える最初にすべきことは 'BlockingQueue'です。プロデューサはオブジェクトをキューに入れてループします(オブジェクトはこの場合はIntegerオブジェクトになります)。そして、コンシューマはオブジェクトを取り出します。コンシューマがオブジェクトを削除しようとしたときにキューが空の場合、コンシューマはオブジェクトが使用可能になるまでブロックされます。また、キューに上限の制限(多くの場合、良いアイデア)がある場合、キューがいっぱいになるたびにプロデューサがブロックされます。 –

+0

@ jameslarge:ありがとうございました! :) BlockingQueueを調べる –

答えて

1

スリープタイムアウトは厳密であることが保証されていないため、1つのスレッドが後でスリープ状態になり、早く復帰することが絶対に有効です。 Thread.sleepのJavaドキュメントからの引用:システムタイマーとスケジューラ

[...]精度の対象と精度理論的にはそれが二回行動をするために一つのスレッドのためにも可能です、しばらく2番目のスレッドはスリープ状態になります。

あなたは2つのスレッドが次々に行動したい場合は、使用メカニズム待機通知:DOCから

プロデューサー

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.put(i); 
    lock.notifyAll(); 
    lock.wait();  
} 

消費者

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.get(i); 
    lock.notifyAll(); 
    lock.wait();  
} 
+0

だから、私は実際に、wait、notify、notifyallのない単一のロックオブジェクトを使用して同じ機能を使用しようとしていました。したがって、両方のスレッドが同じ時間スリープしていて、スレッド1がスレッド2より早くスリープすると仮定すると、スレッド2はスリープ状態になります(x ms後にスリープすると仮定します)。スレッド1が起きると、JVMは他のスレッドが存在しないので、スレッド1に制御権を与えます。一方、スレッド2は起きており、スレッド1によってロックが取られたときに待機する必要があります。そして、これは続くはずです...私はこの理解に間違っていますか? –

+0

@PriyanshGoelスリープを使用したくない場合は、2つのスレッドの間にギャップを作ってみてください。たとえば、最初のスレッドよりも500ms遅れて2番目のスレッドを開始します。 – AdamSkywalker

+0

-1は、初心者にwait()/ notify()/ notifyAllそれらが使用されることを意図していない方法で()メカニズムを使用します。待機/通知は、このサイトのnoobsの混乱の主な原因です。これは、より高度な同期オブジェクトを実装するために使用される低レベルのプリミティブです(https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)。ほとんどの問題(これを含む)は、標準ライブラリによって提供される上位レベルのオブジェクトを使用して解決できます。 (例えば、 'BlockingQueue'はこの場合うまくいくでしょう) –

2

を:

...ただし、これらのスリープ時間は、 の基になるOSによって提供される機能によって制限されているため、正確であるとは限りません。いずれにしても、スリープを呼び出すと はスレッドを一時停止しますあなたは、「プロデューサー」と「消費者」を聞くたびに、正確に時間が

https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

+0

ありがとうございます! :) –