Executors.newCachedThreadPool () против Executors.newFixedThreadPool () - PullRequest
134 голосов
/ 04 июня 2009

newCachedThreadPool() против newFixedThreadPool()

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

Ответы [ 7 ]

173 голосов
/ 04 июня 2009

Я думаю, что документы достаточно хорошо объясняют разницу и использование этих двух функций:

newFixedThreadPool

Создает пул потоков, который повторно использует фиксированное количество работающих потоков общая неограниченная очередь. В любом Точка, не более чем nThreads темы будут быть активным в обработке задач. Если дополнительные задачи представляются при все темы активны, они будут ждать в очереди, пока поток не будет имеется в наличии. Если какой-либо поток завершается из-за сбоя во время исполнения до выключения, новый примет его место, если необходимо выполнить последующие задачи. Нити в Пул будет существовать до тех пор, пока не будет явно неисправность.

newCachedThreadPool

Создает пул потоков, который создает новые темы по мере необходимости, но будут использовать повторно ранее построенные потоки, когда они доступны. Эти бассейны будут как правило, улучшить производительность программы, которые выполняют много краткосрочных асинхронные задачи. Звонки для выполнения будет использовать ранее построенный темы, если доступны. Если не существует тема доступна, новая тема будет быть создан и добавлен в пул. Темы, которые не были использованы для шестьдесят секунд прекращаются и удаляется из кеша. Таким образом, бассейн который остается бездействующим достаточно долго не потреблять никаких ресурсов. Обратите внимание, что бассейны с похожими свойствами, но разные детали (например, параметры тайм-аута) могут быть созданы используя конструкторы ThreadPoolExecutor.

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

Учитывая это, потребление ресурсов будет очень сильно зависеть от ситуации. Например, если у вас огромное количество долгосрочных задач, я бы предложил FixedThreadPool. Что касается CachedThreadPool, в документах говорится, что «эти пулы обычно улучшают производительность программ, которые выполняют много кратковременных асинхронных задач».

62 голосов
/ 15 декабря 2015

Просто, чтобы завершить остальные ответы, я хотел бы процитировать «Эффективная Java», 2-е издание, Джошуа Блоха, глава 10, пункт 68:

"Выбор службы исполнителя для конкретного приложения может быть сложным. Если вы пишете небольшую программу или слегка загруженный сервер , используя Executors.new - CachedThreadPool - это , как правило, хороший выбор , так как он не требует настройки и, как правило, "делает правильные вещи". Но пул кэшированных потоков не является хорошим выбором для сильно загруженный производственный сервер !

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

Следовательно, на сильно загруженном производственном сервере , вам гораздо лучше использовать Executors.newFixedThreadPool , который дает вам пул с фиксированным числом потоков, или использовать ThreadPoolExecutor класс напрямую, для максимального контроля."

11 голосов
/ 06 сентября 2015

Если вы увидите код в grepcode, вы увидите, что они вызывают ThreadPoolExecutor. внутри и устанавливают свои свойства. Вы можете создать свой, чтобы лучше контролировать ваши требования.

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

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
8 голосов
/ 25 августа 2015

Правильно, Executors.newCachedThreadPool() - не лучший выбор для серверного кода, который обслуживает несколько клиентов и одновременных запросов.

Почему? С этим связаны две (связанные) проблемы:

  1. Это неограниченно, что означает, что вы открываете дверь для того, чтобы кто-то мог повредить вашу JVM, просто добавляя больше работы в службу (атака DoS). Потоки потребляют немалый объем памяти, а также увеличивают потребление памяти в зависимости от их незавершенной работы, поэтому таким способом довольно легко можно свалить сервер (если только у вас нет других автоматических выключателей).

  2. Неограниченная проблема усугубляется тем фактом, что Исполнителю предшествует SynchronousQueue, что означает прямую передачу обслуживания между исполнителем задач и пулом потоков. Каждая новая задача будет создавать новый поток, если все существующие потоки заняты. Как правило, это плохая стратегия для серверного кода. Когда процессор загружается, выполнение существующих задач занимает больше времени. Тем не менее, отправляется больше задач и создается больше потоков, поэтому выполнение задач занимает все больше и больше времени. Когда процессор загружен, больше потоков - это совсем не то, что нужно серверу.

Вот мои рекомендации:

Использовать пул потоков фиксированного размера Executors.newFixedThreadPool или ThreadPoolExecutor. с заданным максимальным количеством потоков;

7 голосов
/ 28 ноября 2015

Если вас не волнует неограниченная очередь из Callable / Runnable задач, вы можете использовать одну из них. По предложению Бруно, я тоже предпочитаю от newFixedThreadPool до newCachedThreadPool между этими двумя.

Но ThreadPoolExecutor предоставляет более гибкие функции по сравнению с newFixedThreadPool или newCachedThreadPool

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

Преимущества:

  1. У вас есть полный контроль над размером BlockingQueue . Это не безгранично в отличие от предыдущих двух вариантов. Я не выйду из памяти из-за огромного скопления отложенных задач Callable / Runnable в неожиданной турбулентности в системе.

  2. Вы можете реализовать пользовательскую Обработка отклонения политика ИЛИ использовать одну из политик:

    1. В ThreadPoolExecutor.AbortPolicy по умолчанию обработчик генерирует исключительную ситуацию RejectedExecutionException при отклонении.

    2. В ThreadPoolExecutor.CallerRunsPolicy поток, который вызывает выполнение, запускает задачу. Это обеспечивает простой механизм управления с обратной связью, который замедляет скорость отправки новых задач.

    3. В ThreadPoolExecutor.DiscardPolicy задача, которая не может быть выполнена, просто отбрасывается.

    4. В ThreadPoolExecutor.DiscardOldestPolicy, если исполнитель не выключен, задача во главе рабочей очереди отбрасывается, а затем повторяется попытка выполнения (что может снова привести к сбою, что приведет к повторению.)

  3. Вы можете реализовать собственную фабрику нитей для следующих случаев использования:

    1. Чтобы установить более понятное имя потока
    2. Для установки статуса демона потока
    3. Чтобы установить приоритет потока
3 голосов
/ 08 мая 2014

Вы должны использовать newCachedThreadPool только тогда, когда у вас есть недолговечные асинхронные задачи, как указано в Javadoc. Если вы отправляете задачи, для обработки которых требуется больше времени, вы в конечном итоге создадите слишком много потоков. Вы можете использовать 100% ЦП, если будете отправлять долго выполняющиеся задачи с более высокой скоростью в newCachedThreadPool (http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/).

0 голосов
/ 13 марта 2018

Я делаю несколько быстрых тестов и получаю следующие выводы:

1) при использовании SynchronousQueue:

После того, как потоки достигнут максимального размера, любая новая работа будет отклонена за исключением, как показано ниже.

Исключение в потоке "основной" , поставленные в очередь задачи = 0, выполненные задачи = 0]

в java.util.concurrent.ThreadPoolExecutor $ AbortPolicy.rejectedExecution (ThreadPoolExecutor.java:2047)

2) при использовании LinkedBlockingQueue:

Потоки никогда не увеличиваются от минимального размера до максимального размера, что означает, что пул потоков имеет фиксированный размер в качестве минимального размера.

...