Каков вариант использования неограниченной очереди в Java Executors?

Фабрика Executors от Java использует неограниченную очередь ожидающих задач. Например, Executors.newFixedThreadPool использует new LinkedBlockingQueue, у которого нет ограничений на принятие задач.

public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}

Когда поступает новая задача и нет доступного потока, она помещается в очередь. Задачи можно добавлять в очередь на неопределенное время, вызывая OutOfMemoryError.

Каков на самом деле сценарий использования этого подхода? Почему создатели Java не использовали ограниченную очередь? Я не могу представить себе сценарий, когда неограниченное лучше ограниченного, но я могу что-то упустить. Может ли кто-нибудь дать достойное объяснение? Лучший!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
0
1 490
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете выполнять задачи отклонять, используя ArrayBlockingQueue (ограниченная очередь блокировки)

final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
executorService = new ThreadPoolExecutor(n, n,
    0L, TimeUnit.MILLISECONDS,
    queue);

Code above is equivalent to Executors.newFixedThreadPool(n), however instead of default unlimited LinkedBlockingQueue we use ArrayBlockingQueue with fixed capacity of 100. This means that if 100 tasks are already queued (and n being executed), new task will be rejected with RejectedExecutionException.

Да, именно это я имел в виду. Я считаю, что фабричные методы из Executors должны фактически использовать ArrayBlockingQueue и ожидать параметр, определяющий его длину. Таким образом, разработчикам будет более очевидно, что происходит за кулисами.

Dariusz Mydlarz 24.12.2018 22:20
Ответ принят как подходящий

Это подход по умолчанию, и пользователь может выбрать переход в ограниченную очередь.

Теперь, возможно, ваш вопрос: почему это значение по умолчанию?

На самом деле работать с ограниченными очередями сложнее, что бы вы сделали, если очередь заполнена? Вы бросаете задачу и не принимаете ее? Вы генерируете исключение и завершаете весь процесс ошибкой? Разве это не то, что случилось бы в случае OOM? Таким образом, все это решение должен принимать пользователь, который принимает множество длительных задач, а не является пользователем Java по умолчанию.

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

Можете ли вы привести хорошие примеры, когда неограниченные очереди - разумный подход? Когда разработчик может игнорировать "ограничения" и не заботиться о емкости приложения, его памяти и т. д. Неограниченная очередь в конечном итоге приведет его к OOM или проблемам с производительностью ...

Dariusz Mydlarz 24.12.2018 22:22

Спасибо, но все равно звучит как искусство, а не наука. Тем не менее, спасибо за вклад

Dariusz Mydlarz 26.12.2018 08:26

Tasks can be added to the queue indefinitely causing OutOfMemoryError

Нет. Очередь на самом деле не unbouned, для неограниченного LinkedBlockingQueue, это capacity - это Integer.MAX_VALUE(2147483647). Когда недостаточно места, RejectedExecutionHandler будет обрабатывать новые задачи прибытия. И обработчик по умолчанию - AbortPolicy, он прерывает новые задачи напрямую.

I can't imagine a scenario when unbounded is better the bounded

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

Если вам это небезразлично, вы можете создать ThreadPoolExecutor с помощью специального конструктора.

Спасибо, но я до сих пор не вижу допустимого и разумного варианта использования «неизвестной» очереди с ограничениями, которая может взорваться в любой момент, чего вы не ожидали .. Тем не менее, спасибо за ваш вклад

Dariusz Mydlarz 24.12.2018 22:18

Поскольку вы спрашиваете о «сценарии использования», все очень просто: каждый раз, когда у вас есть много отдельных задач, которые вы хотите в конечном итоге завершить. Скажем, вы хотите загрузить сотни тысяч файлов? Создайте задачу загрузки для каждого, отправьте в ExecutorService, дождитесь завершения. В конечном итоге задачи будут завершены, поскольку вы больше не добавляете их, и нет причин для ограничения.

Это звучит как лучший ответ, но все же не решает проблему. Рано или поздно вы достигнете предела, и у вас не будет контроля над ним (потому что вы не предоставили свой собственный обработчик отклонения и т. д.). Мне кажется, это плохой дефолт от JDK ...

Dariusz Mydlarz 24.12.2018 22:17

Другие вопросы по теме