newCachedThreadPool()
против newFixedThreadPool()
Когда я должен использовать один или другой? Какая стратегия лучше с точки зрения использования ресурсов?
newCachedThreadPool()
против newFixedThreadPool()
Когда я должен использовать один или другой? Какая стратегия лучше с точки зрения использования ресурсов?
Я думаю, что документы достаточно хорошо объясняют разницу и использование этих двух функций:
Создает пул потоков, который повторно использует фиксированное количество потоков, работающих в общей неограниченной очереди. В любой момент не более nThreads потоков будут активными задачами обработки. Если дополнительные задачи отправляются, когда все потоки активны, они будут ждать в очереди, пока поток не станет доступным. Если какой-либо поток завершается из-за сбоя во время выполнения до завершения работы, новый будет занимать его место, если это необходимо для выполнения последующих задач. Потоки в пуле будут существовать до тех пор, пока он не будет явно отключен.
Создает пул потоков, который создает новые потоки по мере необходимости, но будет повторно использовать ранее созданные потоки, когда они станут доступны. Эти пулы обычно улучшают производительность программ, которые выполняют много кратковременных асинхронных задач. Вызовы для выполнения будут повторно использовать ранее созданные потоки, если они доступны. Если существующий поток недоступен, новый поток будет создан и добавлен в пул. Потоки, которые не использовались в течение шестидесяти секунд, завершаются и удаляются из кэша. Таким образом, пул, который остается бездействующим достаточно долго, не будет потреблять никаких ресурсов. Обратите внимание, что пулы с похожими свойствами, но с разными деталями (например, параметры времени ожидания) могут быть созданы с помощью конструкторов ThreadPoolExecutor.
С точки зрения ресурсов, newFixedThreadPool
все потоки будут работать до тех пор, пока они не будут явно завершены. В newCachedThreadPool
потоках, которые не использовались в течение шестидесяти секунд, прекращаются и удаляются из кэша.
Учитывая это, потребление ресурсов будет очень сильно зависеть от ситуации. Например, если у вас огромное количество долгосрочных задач, я бы предложил FixedThreadPool
. Что касается CachedThreadPool
документов, они говорят, что «эти пулы обычно улучшают производительность программ, которые выполняют много кратковременных асинхронных задач».
newCachedThreadPool
может вызвать некоторые серьезные проблемы, потому что вы оставляете весь контроль надthread pool
и когда служба работает с другими на одном хосте , что может вызвать сбой других из-за долгого ожидания ЦП. Поэтому я думаю, чтоnewFixedThreadPool
может быть более безопасным в такого рода сценарии. Также этот пост проясняет самые выдающиеся различия между ними.Просто чтобы завершить остальные ответы, я хотел бы процитировать «Эффективная Java», 2-е издание, Джошуа Блоха, глава 10, пункт 68:
источник
Если вы посмотрите на исходный код , вы увидите, что они вызывают ThreadPoolExecutor. внутренне и устанавливая их свойства. Вы можете создать свой, чтобы лучше контролировать ваши требования.
источник
Если вас не волнует неограниченная очередь задач Callable / Runnable , вы можете использовать одну из них. Как предложил Брюно, я тоже предпочитаю ,
newFixedThreadPool
чтобыnewCachedThreadPool
над этими двумя.Но ThreadPoolExecutor обеспечивает более гибкие возможности по сравнению с любым
newFixedThreadPool
илиnewCachedThreadPool
Преимущества:
У вас есть полный контроль над размером BlockingQueue . Это не безгранично, в отличие от двух предыдущих вариантов. Я не получу ошибку нехватки памяти из-за огромного скопления отложенных задач Callable / Runnable, когда в системе возникает непредвиденная турбулентность.
Вы можете реализовать собственную политику обработки отклонений ИЛИ использовать одну из политик:
По умолчанию
ThreadPoolExecutor.AbortPolicy
обработчик генерирует исключительную ситуацию RejectedExecutionException при отклонении.В
ThreadPoolExecutor.CallerRunsPolicy
, поток, который вызывает выполнение, выполняет задачу. Это обеспечивает простой механизм управления с обратной связью, который замедляет скорость отправки новых задач.В
ThreadPoolExecutor.DiscardPolicy
, задача, которая не может быть выполнена, просто отбрасывается.В
ThreadPoolExecutor.DiscardOldestPolicy
, если исполнитель не выключается, задача во главе очереди работы отбрасывается, а затем выполнение повторяется (который может потерпеть неудачу снова, в результате чего это будет повторяться.)Вы можете реализовать собственную фабрику потоков для следующих случаев использования:
источник
Это верно,
Executors.newCachedThreadPool()
не лучший выбор для серверного кода, который обслуживает несколько клиентов и одновременных запросов.Зачем? Есть в основном две (связанные) проблемы с этим:
Он не ограничен, что означает, что вы открываете дверь для любого, кто может нанести вред вашей JVM, просто добавляя больше работы в службу (атака DoS). Потоки потребляют немалый объем памяти, а также увеличивают потребление памяти в зависимости от их незавершенной работы, поэтому таким способом довольно легко опустить сервер (если у вас нет других автоматических выключателей).
Неограниченная проблема усугубляется тем фактом, что Executor находится перед a,
SynchronousQueue
что означает прямую передачу обслуживания между исполнителем задач и пулом потоков. Каждая новая задача создает новый поток, если все существующие потоки заняты. Как правило, это плохая стратегия для серверного кода. Когда процессор загружается, выполнение существующих задач занимает больше времени. Тем не менее, отправляется больше задач и создается больше потоков, поэтому выполнение задач занимает все больше времени. Когда процессор загружен, больше потоков - это совсем не то, что нужно серверу.Вот мои рекомендации:
Используйте пул потоков фиксированного размера Executors.newFixedThreadPool или ThreadPoolExecutor. с заданным максимальным количеством потоков;
источник
ThreadPoolExecutor
Класс является базовой для реализации исполнителей, которые возвращаются из многихExecutors
фабричных методов. Итак, давайте подойдем к пулам потоков Fixed и CachedThreadPoolExecutor
с точки зрения России.ThreadPoolExecutor
Главный конструктор этого класса выглядит следующим образом :
Размер основного пула
corePoolSize
Определяет минимальный размер пула целевой нити. Реализация будет поддерживать пул такого размера, даже если нет задач для выполнения.Максимальный размер пула
maximumPoolSize
Максимальное количество потоков , которые могут быть активными одновременно.После того, как пул потоков увеличивается и становится больше
corePoolSize
порогового значения, исполнитель может завершить свободные потоки иcorePoolSize
снова обратиться к ним . ЕслиallowCoreThreadTimeOut
это правда, то исполнитель может даже завершить потоки основного пула, если они простаивают большеkeepAliveTime
порогового значения.Таким образом, суть в том, что если потоки остаются бездействующими больше
keepAliveTime
порогового значения, они могут быть прерваны, так как на них нет спроса.Queuing
Что происходит, когда приходит новая задача и все основные потоки заняты? Новые задачи будут поставлены в очередь внутри этого
BlockingQueue<Runnable>
экземпляра. Когда поток становится свободным, одна из этих задач в очереди может быть обработана.Существуют разные реализации
BlockingQueue
интерфейса в Java, поэтому мы можем реализовать разные подходы к организации очередей, такие как:Ограниченная очередь . Новые задачи будут поставлены в очередь в ограниченной задаче.
Неограниченная очередь : новые задачи будут помещаться в очередь в неограниченной очереди задач. Таким образом, эта очередь может вырасти настолько, насколько позволяет размер кучи.
Синхронная передача обслуживания : мы также можем использовать
SynchronousQueue
для постановки новых задач в очередь. В этом случае при постановке в очередь новой задачи другой поток уже должен ждать этой задачи.Представление работы
Вот как
ThreadPoolExecutor
выполняется новая задача:corePoolSize
запущено меньше потоков, пытается запустить новый поток с заданной задачей в качестве первого задания.BlockingQueue#offer
метод.offer
Метод не будет блокировать , если очередь заполнена и немедленно возвращаетсяfalse
.offer
возвратfalse
), он пытается добавить новый поток в пул потоков с этой задачей в качестве своей первой работы.RejectedExecutionHandler
.Основное различие между фиксированным и кэшированным пулами потоков сводится к следующим трем факторам:
Фиксированный пул потоков
Вот как это
Excutors.newFixedThreadPool(n)
работает:Как вы видете:
OutOfMemoryError
.Пул потоков фиксированного размера кажется хорошим кандидатом, когда мы собираемся ограничить количество одновременных задач для целей управления ресурсами .
Например, если мы собираемся использовать исполнителя для обработки запросов веб-сервера, фиксированный исполнитель может обрабатывать пакеты запросов более разумно.
Для еще лучшего управления ресурсами настоятельно рекомендуется создать кастом
ThreadPoolExecutor
с ограниченнойBlockingQueue<T>
реализацией в сочетании с разумнойRejectedExecutionHandler
.Пул кэшированных потоков
Вот как это
Executors.newCachedThreadPool()
работает:Как вы видете:
Integer.MAX_VALUE
. Практически, пул потоков не ограничен.SynchronousQueue
всегда терпит неудачу, когда на другом конце нет никого, чтобы принять ее!Используйте его, когда у вас много предсказуемых краткосрочных задач.
источник
Вы должны использовать newCachedThreadPool только тогда, когда у вас есть недолговечные асинхронные задачи, как указано в Javadoc. Если вы отправляете задачи, обработка которых занимает больше времени, вы в конечном итоге создадите слишком много потоков. Вы можете использовать 100% ЦП, если отправляете долго выполняющиеся задачи с более высокой скоростью в newCachedThreadPool ( http://rashcoder.com/be-careful- while-using-executors-newcachedthreadpool/ ).
источник
Я делаю несколько быстрых тестов и получаю следующие выводы:
1) при использовании SynchronousQueue:
После того, как потоки достигнут максимального размера, любая новая работа будет отклонена за исключением, как показано ниже.
2) при использовании LinkedBlockingQueue:
Потоки никогда не увеличиваются от минимального размера до максимального размера, что означает, что пул потоков имеет фиксированный размер в качестве минимального размера.
источник