Условия Java с блокировками

#java #multithreading #blocking #locks

Вопрос:

Документы Java отсюда https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

Я пытаюсь понять концепцию Условий. В приведенном ниже коде поместите и возьмите оба ожидания на один и тот же замок. Док говорит это перед вызовом любого условия.await() нам нужно заполучить соответствующий замок (here line 2 : final Lock lock = new ReentrantLock();).

Мой вопрос в том, допустим, буфер заполнен. В этом случае, если мы попытаемся вставить объект, поток будет заблокирован, пока count == items.length , но блокировка здесь все еще остается с потоком, который пытается вставить. Как мы можем вызвать метод take (), чтобы уменьшить количество, так как take() должен получить доступ к той же блокировке. Мне кажется, что это будет тупик, так как take() нельзя вызвать, то же самое, если элементы пусты, мы не сможем вызвать put (), так как теперь блокировка уже с помощью take().

Пожалуйста, помогите мне понять, если я что-то упускаю.Заранее спасибо.

 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();
         }
       }
     }
 

Комментарии:

1. await()-javadoc : «Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается для целей планирования потоков и бездействует, пока не произойдет одно из четырех событий:…», пожалуйста, также «мелкий шрифт» 😉

Ответ №1:

Если вы посмотрите на документ api для условия#ожидание ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html) там написано:

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

Поскольку замок снят, нет никаких проблем с тем, чтобы поставить блокировку.

Комментарии:

1. так это похоже на вызов функции wait() для любого объекта. Я имею в виду, что вместо блокировки мы можем синхронизировать объект, скажем obj, а затем дождаться этого объекта.подождите()

2. @cjava: да, это та же идея, за исключением того, что возможность иметь разные условия для одной блокировки является большим преимуществом. И synchronized тоже использует блокировку, это неявная блокировка объекта.

3. Я предполагаю, что использование обратного отсчета здесь будет лучше, потому что сигнал вызова не будет полностью гарантировать пробуждение заблокированного потока. что вы посоветуете

4. @cjava: сигнал вызова означает, что поток, ожидающий этого условия, получает уведомление. С синхронизацией тогда, если потоки ждут по разным причинам, есть вероятность, что поток получит пробуждение, когда он не сможет добиться прогресса. Но здесь это не проблема, если вы используете условия надлежащим образом, как это имеет место в опубликованном коде.

5. из документа, спасибо, последний пункт, вы имеете в виду, что сигнал разбудит тот же поток: блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается для целей планирования потоков и бездействует до тех пор, пока не произойдет одно из четырех событий: какой-то другой поток вызывает метод signal() для этого условия, и текущий поток выбирается в качестве потока для пробуждения; или