У меня есть 3 потока: 2 потребителя, ConsumerA
и ConsumerB
, и a Producer
.
У меня также есть a LinkedBlockingQueue queue
В t=1: ConsumerA
вызовы queue.take ()
В t=2: Consumer
B называет queue.take ()
В t=3: Producer
вызовы queue.put (нечто)
Гарантируется, что ConsumerA получает нечто перед ConsumerB? Другими словами, порядок, в котором потребители вызывает take()
порядок, в котором каждый уведомляется?
В противном случае существует ли альтернативная структура данных, которая отдаст более высокий приоритет на основе порядка?
Судя по исходному коду, это не гарантируется. Имеется механизм защищенного блока с случайным пробуждением одного потока, в зависимости от того, как себя чувствует планировщик.
notEmpty.signal(); // propagate to a non-interrupted thread
Полный код: http://kickjava.com/src/java/util/concurrent/LinkedBlockingQueue.java.htm
Изменить: только что снова взглянул на ReenterantLock и Condition, потоки сигнализируются в порядке FIFO , видимо. Таким образом, первый поток, ожидающий вставки, будет сигнализирован первым. Однако это детали реализации! Не надейтесь на них.
реализация не требуется для определить точно такие же гарантии или семантика для всех трех форм ожидание, при этом не требуется поддержки прерывание фактической приостановки из потока
Во многих случаях порядок потоков, поступающих в очередь, будет в соответствующем порядке. Однако очередь LinkedBlocking использует несправедливую блокировку. Это позволяет потокам вмешиваться друг в друга. Хотя в редких случаях может случиться следующее.
Это одна из возможностей.
Итак, копаемся в недрах исходного кода Java 6 (пропустите часть между горизонтальными правилами, если вас не волнует фактический код, ответственный за этот материал)
java.util.concurrent.LinkedBlockingQueue
реализует мьютекс, используя экземпляры java.util.concurrent.locks.ReentrantLock
.
В свою очередь, переменные синхронизации создаются с использованием java.util.concurrent.locks.ReentrantLock.newCondition ()
, который вызывает java.util.concurrent.locks.ReentrantLock $ Sync.newCondition ()
.
Метод java.util.concurrent.locks.ReentrantLock $ Sync.newCondition ()
возвращает экземпляр java.util.concurrent.AbstractQueuedSynchronizer $ ConditionObject
, который реализует обычную синхронизацию вызовы переменных к await ()
, signal ()
и signalAll ()
, описываемым интерфейсом java.util.concurrent.locks.Condiion
.
Глядя на исходный код класса ConditionObject
, он сохраняет два члена с именами firstWaiter
и lastWaiter
, которые являются первым и последним узлами в блокировке CLH. очередь (экземпляры java.util.concurrent.locks.AbstractQueuedSynchronizer $ Node
).
В документации этого класса говорится:
Поток может попытаться получить, если он первый в очереди. Но быть первым не гарантирует успеха; это только дает право на споры. Таким образом, опубликованному в настоящее время потоку претендентов, возможно, придется подождать еще раз.
Я считаю, что ответ здесь заключается в том, что метод take ()
в LinkedBlockingQueue
пытается предоставить предпочтение тем потокам, которые ранее вызывали take ()
. Это даст первому потоку для вызова take ()
первый шанс захватить элемент очереди, когда он станет доступным, но из-за тайм-аутов, прерываний и т. Д. Этот поток не гарантируется быть первым потоком, который получит элемент из очереди.
Имейте в виду, что это полностью зависит от конкретной реализации. В общем, вы должны предполагать, что вызовы take ()
разбудят случайный ожидающий поток, когда становится доступным элемент очереди, и не обязательно первый, который вызвал take ()
.