2016-03-25 3 views
0

私はマルチスレッドのトピックであるJavaのより高度なトピックを勉強しています。ロックオブジェクトを使用してメンバー変数を同期する

多くのコードでは、別のオブジェクトロックObject lock = new Object();を使用して、一部のクラスデータメンバーを同期させています。

package multithreading; 

import java.util.LinkedList; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

class ProduceConsume { 

    private LinkedList<Integer> queue = new LinkedList<>(); 
    private final int LIMIT = 10; 
    private final Object lock = new Object(); 

    public void produce() throws InterruptedException { 
     int value = 0; 

     while (true) { 

      synchronized (lock) { 
       while (queue.size() == LIMIT) { 
        lock.wait(); 
       } 

       queue.add(value++); 
       lock.notify(); 
      } 
     } 
    } 

    public void consume() throws InterruptedException { 
     while (true) { 
      Thread.sleep(1000); 

      synchronized (lock) { 
       while (queue.size() == 0) { 
        lock.wait(); 
       } 

       System.out.print("Size is: " + queue.size()); 
       int value = queue.removeFirst(); 
       System.out.println("; value is: " + value); 

       lock.notify(); 
      } 
     } 
    } 

} 


public class ProducerConsumerWaitNotify { 

    public static void main(String[] args) throws InterruptedException { 

     ProduceConsume object = new ProduceConsume(); 

     ExecutorService execuor = Executors.newFixedThreadPool(2); 

     execuor.submit(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        object.produce(); 
       } catch (InterruptedException ex) { 
        Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex); 
       } 
      } 
     }); 
     execuor.submit(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        object.consume(); 
       } catch (InterruptedException ex) { 
        Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex); 
       } 
      } 
     }); 

     execuor.shutdown(); 
     execuor.awaitTermination(1000, TimeUnit.DAYS); 
    } 
} 

なぜLinkedListオブジェクト自体をロックするべきではないのですか?これは私がこの手法を使って見た唯一の例ではありません。それは良い練習ですか?

しかし私は、プロダクションと消費の2つのクラスがあり、リンクされたリストをコンストラクタのメンバーとして受け取ると、このリンクされたリストオブジェクトで同期する必要があります。

concurrentパッケージにはスレッドセーフなクラスがあることは知っていますが、これは私の質問ではありません。上記の2つの方法の間のベストプラクティスについて質問していますか?

+1

オブジェクトをロックするには、final修飾子を使用する必要があります。そうしないと、ある時点で 'lock'参照ポイントを新しいオブジェクトにすることは可能です。だから、異なるモニターでシンクロナイズすることは可能です。それはまったく同期しないのと同じです。 –

+1

以下の答えに埋もれている重要なアイデアは、あなたがあなたのロックオブジェクトに使うものはどれでも、それは 'private'でなければならないということです。クライアントクラスに、ロックのために使用するものと同じオブジェクトに対してロックを行う機能を与えることは決して望ましくありません。理想的な世界では、クライアントプログラマーはそれをしないほどスマートになりますが...とにかく、誰かのプログラムがデッドロックやその他の悪い行為に陥るリスクを軽減します。 –

答えて

0
public class ProduceConsumerTest { 

    private final int poolsize = 10; 
    ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<Integer>(poolsize); 

    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     ProduceConsumerTest produceConsumerTest = new ProduceConsumerTest(); 
     Producer producer = produceConsumerTest.new Producer(); 
     Consumer consumer = produceConsumerTest.new Consumer(); 
     producer.start(); 
     consumer.start(); 
    } 

    class Consumer extends Thread{ 

     @Override 
     public void run(){ 
      Consume(); 
     } 

     public void Consume(){ 
      while(true){ 
       synchronized (arrayBlockingQueue) { 
        while(arrayBlockingQueue.size() == 0){ 
         try { 
          arrayBlockingQueue.wait(); 
         } catch (InterruptedException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
          arrayBlockingQueue.notify(); 
         } 
        } 
        arrayBlockingQueue.poll(); 
        arrayBlockingQueue.notify(); 
        System.out.println("Consuming:Current size:"+arrayBlockingQueue.size()); 
       } 
      } 
     } 
    } 

    class Producer extends Thread{ 

     @Override 
     public void run(){ 
      produce(); 
     } 

     private void produce(){ 
      while(true){ 
       synchronized (arrayBlockingQueue) { 
        while(arrayBlockingQueue.size() == poolsize){ 
         try { 
          arrayBlockingQueue.wait(); 
         } catch (InterruptedException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
          arrayBlockingQueue.notify(); 
         } 
        } 
        arrayBlockingQueue.offer(1); 
        arrayBlockingQueue.notify(); 
        System.out.println("Producing, Current Size:"+arrayBlockingQueue.size()); 
       } 
      } 
     } 

    } 
} 

それはプライベートクラスのメンバーとあなたですとだけあなたはそれをロックすることができますので、あなたは、オブジェクトqueue

+0

'arrayBlockingQueue'はすでにスレッドセーフなので、考える必要はないと思います。私はオブジェクト自体に同期することができますが、ベストプラクティスは何かを知っています。 –

1

することができます、上でロックすることができます。それをうまく管理すれば、クラスのインスタンスを使うだけでデッドロックを引き起こすことはできません。フィールドが公開されている場合、ユーザーはそのフィールドにロックを設定できます。クラス内の同期に同じフィールドを使用すると、デッドロックが発生する可能性があります。だから私たちはこのポインタにロックをかけません。

しかしロックするための個々のオブジェクトを使用するには、いくつかの理由があります。

あなたがアクセスをシリアル化するために(パブリックアクセスをシリアル化するための)controlLockerObject、listLockerObject(リストへのアクセスをシリアル化するために)、updateLockerObject(のようなオブジェクトに名前を付けることができます

オブジェクトをfinalとして宣言することができます。したがって、誤って同期に使用されたオブジェクトを置き換えたり削除したりすることはできません。

1

なぜLinkedListオブジェクト自体をロックしてはいけないのですか?

LinkedListオブジェクトをロックしても、同じ同期の利点が得られます。私は生産のための2つの別々のクラスを持っていると 消費し、それが彼らのコンストラクタのメンバーとしてリンクリストを取る場合、私は私が右アム、このリンクリストのオブジェクトで同期しなければならない こと?感じしかし

私はそれが唯一の選択肢ではありませんので、あなたがリンクリストに同期する必要があります言うことはありません。他のsingletonオブジェクトまたは同じ/異なるクラスのstaticフィールド、またはsynchronize(Object.class)/synchronize(String.class) etcという簡単なフィールドで同期するために、プロデューサスレッドとコンシューマスレッドの両方を持つことができます。

上記の2つの方法のベストプラクティスについて質問していますか?private lock(使用しているもの)を使用しての長所と短所について

、あなたは、Java monitor pattern section of Concurrency In Practice経由のJava並行処理に最適なリソースを行くことをお勧めします。

1

明示的なロックオブジェクトを持つ(最終的にする)ことは問題ありません。 「ああ、彼らはここでロックしていますか、あまりにもすべきです」ということをすぐに知ることができるので、ドキュメンテーションとして機能します。これは、そのコードの将来の開発者が誤って同期をバイパスすることを防ぐことができます。

関連する問題