Я не понимаю, следующий фрагмент Java Параллелизм на практике книге:Сложность операции notifyAll
Использование notifyAll когда только один поток может добиться прогресса неэффективна - иногда немного, иногда грубо так. Если в очереди состояния ждут десять потоков, вызов notifyAll заставляет каждого из них проснуться и бороться за блокировку; то большинство или все они снова вернутся спать. Это означает множество контекстных переключателей и множество конкурирующих блокировок для каждого события, которое позволяет (возможно) создать один поток для достижения прогресса. (В худшем случае, используя notifyAll результаты в O (п) пробуждений, где п будет достаточно.)
Пример кода в листинге 14.6:
@ThreadSafe
public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
// CONDITION PREDICATE: not-full (!isFull())
// CONDITION PREDICATE: not-empty (!isEmpty())
public BoundedBuffer(int size) { super(size); }
// BLOCKS-UNTIL: not-full
public synchronized void put(V v) throws InterruptedException {
while (isFull())
wait();
doPut(v);
notifyAll();
}
// BLOCKS-UNTIL: not-empty
public synchronized V take() throws InterruptedException {
while (isEmpty())
wait();
V v = doTake();
notifyAll();
return v;
}
}
Мы можем, например, , следующая последовательность событий:
- две потребительские потоки пытаются получить объект из буфера, буфер пуст, поэтому они приостановлены.
- 10 изготовителей помещают 10 буферов в буфер, емкость буфера равна 10.
- Производители 100001 пытаются поставить в буфер 100001 объектов, буфер заполнен, поэтому они приостановлены.
- Первый потребитель получает объект из буфера и вызывает notifyAll.
- производитель помещает объект в буфер и вызывает notifyAll, буфер заполнен.
Теперь только одна нить может прогрессировать - потребительская нить. У нас также есть 100000 производителей, которые не могут добиться прогресса.
Не понимаю, почему в худшем случае будет O (n) пробуждения, прежде чем поток, который может добиться прогресса, проснулся.
Я думаю, что в худшем случае является следующая последовательность
- Все потоки просыпаются (из-за notifyAll). Мы «использовали» O (n) пробуждения.
- Нить производителя получает замок, другие потоки приостановлены. Ничья производителя не может прогрессировать, поэтому она приостанавливается и освобождает блокировку.
- Теперь только один производителя нить проснулся, потому что используется другой механизм (поток возобновляет выполнение, так как он получает блокировку - но на этот раз notifyAll является не называется). Мы «используем» только O (1) пробуждения.
- Второй производитель не может добиться прогресса, поэтому он приостановлен и освобождает замок.
- Подобные события происходят для всех других ожидающих производителей.
- Наконец-то просверлена нить, которая может прогрессировать (потребительская нить).
Я думаю, что мы использовали «O (n) + O (n) * O (1) = O (n) пробуждения.
Есть ошибки в книге, или я что-то пропустил здесь?
Возможно, я понимаю вашу точку зрения. Если мы сделаем n puts, и каждый put сделает n пробуждений - у нас есть O (n * n) пробуждения. –
Но я вижу другую проблему - книга может быть яснее. Неясно, что именно означает «n». Я думал, что «n» - это количество потоков, а НЕ количество операций (я думаю, вы предполагаете, что n - это количество потоков и количество операций). Я думал, что у нас есть операция SINGLE (put or take), и мы используем notifyall - и мы должны в худшем случае использовать n * n wakeups, чтобы разбудить «правильный» поток и дать ему блокировку. Я буду ждать других ответов, а затем выберите тот, который будет принят. –
У меня есть ответ от автора - n - это количество операций и количество потоков, поэтому этот ответ (и результат «O (n * n)») правильный. –