Подтвердить что ты не робот

Ожидание состояния в блокировке реентера

Следующий код берется из JavaDoc Condition:

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 потока, Потребитель и Производитель, один с помощью take, один put в одном экземпляре BoundedBuffer.

Предположим, что потребитель идет первым, запускает take(), в котором он блокирует lock и теперь циклически на notEmpty.await();.

Как теперь производитель может попасть в метод put() после блокировки lock, который уже удерживается пользователем?

Что мне здесь не хватает? Является ли lock "временно выпущенным", пока поток ожидает на одном из своих условий? И что означает повторное включение блокировки?

4b9b3361

Ответ 1

Оба Lock и synchronized позволяют потоку отказываться от блокировки при ожидании, а другой поток может получить блокировку. Чтобы остановить ожидание, поток должен повторно захватить блокировку.

Примечание. Они не освобождают его полностью, и если вы берете трассировку стека, вы можете иметь несколько потоков, которые, как представляется, удерживают блокировку одновременно, но не более одного из них будут работать (остальное будет блокировать)

Из Condition.await()

Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается для целей планирования потоков и находится в состоянии покоя, пока не произойдет одна из четырех вещей:

  • Некоторый другой поток вызывает метод signal() для этого условия, и текущий поток выбирается как поток, который нужно разбудить; или
  • Некоторый другой поток вызывает метод signalAll() для этого условия; или
  • Некоторые другие потоки прерывают текущий поток, и прерывание приостановки потока поддерживается; или
  • Происходит "ложное пробуждение".

Во всех случаях, прежде чем этот метод сможет вернуть текущий поток, необходимо повторно установить блокировку, связанную с этим условием. Когда поток возвращается, гарантированно удерживать эту блокировку

Ответ 2

Что касается повторного входа, это означает, что поток, который содержит определенную блокировку, может снова получить тот же самый замок. Если это не так, метод synchronized не сможет вызвать другой метод synchronized одного и того же объекта.

Re-entrancy не участвует в понимании вашей проблемы.

Ответ 3

Я тестировал ниже код с одним монитором и ниже, всегда работает лучше - протестирован на 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);