Executors.newCachedThreadPool() по сравнению с Executors.newFixedThreadPool()

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

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

Ответ 1

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

newFixedThreadPool

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

newCachedThreadPool

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

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

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

Ответ 2

Чтобы завершить другие ответы, я хотел бы привести Эффективную Java, 2nd Edition, Джошуа Блоха, глава 10, Пункт 68:

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

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

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

Ответ 3

Если вы увидите код в 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>());
}

Ответ 4

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

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

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

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

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

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

Ответ 5

Если вас не волнует неограниченная очередь задач 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. Чтобы установить приоритет потока

Ответ 6

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

Ответ 7

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

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

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

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]

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

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

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