Забудьте о «гибридном подходе» здесь, это не работает.
Каждый объект имеет неявную блокировку.Это включает в себя объекты классов Lock, таких как ReentrantLock.Вызов wait и notify всегда использует неявную блокировку, эти методы не используют возможности блокировки объекта Lock, для которого вы их вызываете.Методы wait, notify и notifyAll объявлены в Object как native и final.
Чтобы получить wait и notify для работы, вам нужно синхронизировать объект блокировки, и блокировка, выполняемая такими методами, как tryLock, будетне имеет значения, это будет в конечном итоге функционально эквивалентным final Object lock = new Object();
, что еще более запутанно.
Блокирующие объекты имеют свои собственные эквиваленты, если вы используете java.util.concurrent.locks.Lock
, тогда получите условие из блокировки и вызовите await(что соответствует ожиданию) и signal / signalAll (эквиваленту notify / notifyAll).
С объектами блокировки вы можете иметь несколько условий, позволяющих сигнализировать подмножествам потоков, ожидающих блокировки.Как следствие, вам не нужно signalAll где-то близко, так как код с неявной блокировкой требует notifyAll.
Например, если вы посмотрите, как реализовано ArrayBlockingQueue , он использует ReentrantLock, и есть одно условие для потребителей и другое условие для производителей:
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
построеноwith
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
Эквивалентный код, использующий неявную блокировку, должен был бы вызвать notifyAll, чтобы избежать потери уведомлений, потому что мы не знаем, будет ли уведомленный поток производителем или потребителем, но с отдельными условиями, которые мы знаемкакой тип потока будет получать уведомления.Например, код удаления из очереди вызывает сигнал при условии notFull, пробуждая не более одного потока:
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}