31

次のコードは、次のJavaDocConditionから取得したものです。

class BoundedBuffer {
  final Lock lock = new ReentrantLock();
  final Condition notFull  = lock.newCondition(); 
  final Condition notEmpty = lock.newCondition(); 

  final Object[] items = new Object[100];
  int putptr, takeptr, count;

  public void put(Object x) throws InterruptedException {
    lock.lock();
    try {
      while (count == items.length) 
        notFull.await();
      items[putptr] = x; 
      if (++putptr == items.length) putptr = 0;
      ++count;
      notEmpty.signal();
    } finally {
      lock.unlock();
    }
  }

  public Object take() throws InterruptedException {
    lock.lock();
    try {
      while (count == 0) 
        notEmpty.await();
      Object x = items[takeptr]; 
      if (++takeptr == items.length) takeptr = 0;
      --count;
      notFull.signal();
      return x;
    } finally {
      lock.unlock();
    }
  } 
}

コンシューマープロデューサーの2つのスレッドを想像してみてください。1つはを使用しtake、もう1つputはの単一インスタンスにありBoundedBufferます。

コンシューマーが最初に実行take()され、ロックされてからlockループする場合を考えてみましょうnotEmpty.await();

プロデューサーは、コンシューマーによってすでに保持されているをput()ロックすることを超えて、どのようにしてメソッドに入ることができますか?lock

ここで何が欠けていますか?スレッドがその条件の1つを待機している間に、lock「一時的に解放」されますか?そして、ロックの再入可能性は正確にはどういう意味ですか?

4

3 に答える 3

29

両方ともLocksynchronized待機中にスレッドがロックを放棄できるようにし、別のスレッドがロックを取得できるようにします。待機を停止するには、スレッドがロックを再取得する必要があります。

注:完全には解放されません。スタックトレースを取得すると、一度にロックを保持しているように見える複数のスレッドを持つことができますが、最大で1つが実行されます(残りはブロックされます)

Condition.await()から

この条件に関連付けられたロックはアトミックに解放され、現在のスレッドはスレッドスケジューリングの目的で無効になり、次の4つのいずれかが発生するまで休止状態になります。

  • 他のスレッドがこの条件のsignal()メソッドを呼び出し、現在のスレッドがウェイクアップされるスレッドとして選択されます。また
  • 他のスレッドは、この条件に対してsignalAll()メソッドを呼び出します。また
  • 他のいくつかのスレッドが現在のスレッドを中断し、スレッドの一時停止の中断がサポートされます。また
  • 「スプリアスウェイクアップ」が発生します。

いずれの場合も、このメソッドが戻る前に、現在のスレッドはこの条件に関連付けられたロックを再取得する必要があります。スレッドが戻ると、このロックを保持することが保証されます

于 2012-09-25T08:09:52.230 に答える
6

再入可能性に関しては、これは、特定のロックを保持しているスレッドが同じロックを再度取得できることを意味します。そうでない場合、メソッドは同じオブジェクトのsynchronized別のメソッドを呼び出すことができません。synchronized

再入可能性は、問題の理解には関与しません。

于 2012-09-25T08:17:08.433 に答える
-1

以下のコードを単一のモニターでテストしましたが、以下のパフォーマンスは常に優れています-2コアマシンでテストした場合、条件のパフォーマンスは平均で10〜15%低くなります

 final Object sync = new Object();
  AtomicInteger add=new AtomicInteger();
  AtomicInteger remove=new AtomicInteger();
   final Object[] items = new Object[1];
   int putptr, takeptr, count;        

 public void add(Object x) throws InterruptedException {
       add.incrementAndGet();

     synchronized (sync) {

       while (count == items.length)
         sync.wait();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       sync.notify();
        }
   }

   public Object remove() throws InterruptedException {
       remove.incrementAndGet();

     synchronized (sync) {

       while (count == 0)
         sync.wait();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       sync.notify();
       return x;

       }
   }


  public static void main(String[] args) {
    final BoundedBuffer bf=new BoundedBuffer();

    Thread put =new Thread(){
        public void run(){
        try {
            while(true)
            bf.add(new Object());
        } catch (InterruptedException e) {

        }
        }

    };
    put.start();

    Thread take= new Thread(){
        public void run(){
        try {
        while(true)
            bf.remove();
        } catch (InterruptedException e) {

        }
        }

    };
    take.start();

    try {
        Thread.sleep(1000L);
        put.interrupt();
        take.interrupt();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    System.out.println("add:"+bf.add);
    System.out.println("remove:"+bf.remove);
于 2014-04-28T19:06:58.713 に答える