Исполнители — потребность в LinkedBlockingQueue

Когда мы говорим об обработке асинхронных событий с помощью службы Executors, почему при создании нового фиксированного пула потоков используется LinkedBlockingQueue? Приходящие события вообще не зависят друг от друга, так зачем использовать очередь, потому что поток-потребитель все равно будет участвовать в борьбе за получение блокировки? Почему класс Executors не имеет какой-либо гибридной структуры данных (например, параллельной реализации карты), где в большинстве случаев нет необходимости в блокировке?


person userx    schedule 28.04.2013    source источник
comment
Я бы предположил, что это из-за трех вещей: а) это блокирующая структура данных; б) этот тип очереди является неограниченным; c) Порядок FIFO гарантирован.   -  person Andrew Logvinov    schedule 28.04.2013
comment
@AndrewLogvinov - Привет, спасибо за ваш ответ, вопрос не в том, почему у нас есть один из методов с LBQ, а в том, почему у нас нет другого гибкого метода, где вместо LBQ может быть гибридный DS, который реализует карта ?   -  person userx    schedule 28.04.2013
comment
Если использовать карту, какой будет ключ, а какой - значение?   -  person Alexei Kaigorodov    schedule 28.04.2013


Ответы (1)


Есть очень веская причина, по которой исполнитель пула потоков работает с BlockingQueue (кстати, вы не обязаны использовать реализацию LinkedBlockingQueue, вы можете использовать различные реализации BlockingQueue). Очередь должна блокироваться, чтобы приостанавливать рабочие потоки, когда нет задач для выполнения. Эта блокировка выполняется с помощью переменных состояния ожидания, поэтому ожидающие рабочие потоки не потребляют ресурсы ЦП, когда очередь пуста.

Если вы используете неблокирующую очередь в пуле потоков, то как рабочие потоки будут опрашивать задачи для выполнения? Им пришлось бы реализовать какой-то опрос, что является лишней тратой ресурсов процессора (будет "занято ожидание").

ОБНОВИТЬ:

Хорошо, теперь я полностью понял вариант использования. Тем не менее вам все равно нужно блокировать сбор. Причина в основном та же - поскольку вы реализуете Producer-Consumer, у вас должны быть средства для рабочих потоков, чтобы они ждали поступления сообщений - и это вы просто не можете сделать без мьютекса + переменной условия (или просто BlockingQueue).

По поводу карты - да, я понимаю, как вы хотите ее использовать, но, к сожалению, такой реализации не предусмотрено. Недавно решал аналогичную задачу: нужно было сгруппировать входящие задачи по некоторым критериям и последовательно выполнять задачи из каждой группы. В результате я реализовал свой собственный GroupThreadPoolExecutor, который выполняет эту группировку. Идея проста: сгруппировать входящие задачи в карту, а затем добавить их в очередь исполнителя, когда завершится предыдущая задача из группы.

Существует большое обсуждение здесь - я думаю, что это имеет отношение к вашему вопросу.

person nogard    schedule 28.04.2013
comment
@ nogard - Представьте, что существует некоторая реализация параллельной хэш-карты в cyz, где события поступают с pk, который не является +1. Допустим, первый объект события прибыл с номером 5000 и уникальным идентификатором 1. Второй объект события прибыл с номером 10000 и уникальным идентификатором 1. Теперь на основе хеширования, если событие 5000 и событие 10000 могут быть сохранены в разных части массива. В идеале, если 2 разных потока опрашиваются в разных частях массива, нет необходимости в блокировке взятия! - person userx; 28.04.2013
comment
Что ж, спасибо, что указали мне на ссылку и ваш ответ. Группировка задач выглядит нормально (хотя мне еще предстоит глубоко подумать), но я не хочу оказаться в ситуации, когда я вообще связываю определенную группу с определенным потоком. Скажем, если в контракте 'x', события которого 1,2,3..etc поступают по порядку, я хочу, чтобы они обрабатывались параллельно тремя потоками, но доставляли их клиенту в порядке поступления. Я надеялся отсортировать вывод этих потоков, а затем поместить их в окончательную очередь для отправки. - person userx; 28.04.2013