newCachedThreadPool()
против newFixedThreadPool()
p >
Когда я должен использовать тот или иной? Какая стратегия лучше с точки зрения использования ресурсов?
newCachedThreadPool()
против newFixedThreadPool()
p >
Когда я должен использовать тот или иной? Какая стратегия лучше с точки зрения использования ресурсов?
Я думаю, что документы подробно объясняют разницу и использование этих двух функций:
Создает пул потоков, который повторно использует фиксированное количество отключенных потоков общая неограниченная очередь. В любом точка, не более nThreads потоков будет быть активными задачами обработки. Если дополнительные задачи передаются, когда все потоки активны, они будут ждать в очереди, пока поток не будет доступный. Если какой-либо поток завершается из-за сбоя во время выполнения до выключения, новый его место, если это необходимо для выполнения последующие задачи. Нити в пул будет существовать до тех пор, пока он не будет явно неисправность.
Создает пул потоков, который создает новый потоки по мере необходимости, но будут использоваться повторно ранее построенные потоки, когда они доступны. Эти бассейны будут обычно улучшают производительность программы, выполняющие много недолговечных асинхронные задачи. Вызовы для выполнения будет повторно использовать ранее построенные потоки, если они доступны. Если нет поток доступен, новый поток будет быть создан и добавлен в пул. Темы, которые не использовались для шестьдесят секунд прекращаются и удалены из кеша. Таким образом, пул который остается без дела достаточно долго не потреблять никаких ресурсов. Обратите внимание, что бассейны с аналогичными свойствами, но различные детали (например, параметры таймаута). используя конструкторы ThreadPoolExecutor.
С точки зрения ресурсов, newFixedThreadPool
будет поддерживать все потоки, пока они не будут явно закрыты. В newCachedThreadPool
Нити, которые не использовались в течение шестидесяти секунд, завершаются и удаляются из кеша.
Учитывая это, потребление ресурсов будет сильно зависеть от ситуации. Например, если у вас есть огромное количество длительных задач, я бы предложил FixedThreadPool
. Что касается CachedThreadPool
, документы говорят, что "Эти пулы обычно улучшают производительность программ, выполняющих многие недолговечные асинхронные задачи".
Чтобы завершить другие ответы, я хотел бы привести Эффективную Java, 2nd Edition, Джошуа Блоха, глава 10, Пункт 68:
"Выбор службы-исполнителя для конкретного приложения может быть сложным. Если вы пишете небольшую программу или слегка загруженный сервер, используя Executors.new - CachedThreadPool - это хороший, поскольку он не требует настройки и вообще" делает все правильно ". Но пул кэшированных потоков не является хорошим выбором для загруженного рабочего сервера!
В кэшированном пуле потоков, отправленные задачи не помещаются в очередь, но сразу же передаются в поток для выполнения. Если потоки не доступны, создается новый. Если сервер настолько загружен, что все его процессоры полностью используются, и придет больше задач, будет создано больше потоков, что только усугубит ситуацию.
Следовательно, на сильно загруженном производственном сервере вам гораздо лучше использовать Executors.newFixedThreadPool, который дает вам пул с фиксированным количеством потоков или использует класс ThreadPoolExecutor, для максимального контроля."
Если вы увидите код в 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>());
}
Правильно, Executors.newCachedThreadPool()
не является отличным выбором для кода сервера, обслуживающего несколько клиентов и одновременных запросов.
Почему? В основном есть две (связанные) проблемы:
Он неограничен, а это значит, что вы открываете дверь для кого-либо, чтобы нанести ущерб вашей JVM, просто добавив больше работы в службу (DoS-атака). Нити потребляют незначительный объем памяти, а также увеличивают потребление памяти на основе их незавершенного производства, поэтому довольно легко свергнуть сервер таким образом (если у вас нет других автоматических выключателей на месте).
Неограниченная проблема усугубляется тем фактом, что Executor находится под знаком SynchronousQueue
, что означает прямую передачу обслуживания между получателем задачи и пулом потоков. Каждая новая задача создаст новый поток, если все существующие потоки заняты. Это, как правило, плохая стратегия для кода сервера. Когда процессор становится насыщенным, существующие задачи занимают больше времени. Тем не менее, все больше задач отправляется и создается больше потоков, поэтому задачи занимают больше времени и дольше. Когда процессор насыщен, больше потоков определенно не соответствует потребностям сервера.
Вот мои рекомендации:
Использовать пул потоков фиксированного размера Executors.newFixedThreadPool или ThreadPoolExecutor. с установленным максимальным количеством потоков;
Если вас не волнует неограниченная очередь задач Callable/Runnable, вы можете использовать одну из них. По предложению Бруно, я тоже предпочитаю newFixedThreadPool
вместо newCachedThreadPool
между этими двумя.
Но ThreadPoolExecutor предоставляет более гибкие функции по сравнению с newFixedThreadPool
или newCachedThreadPool
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Преимущества:
У вас есть полный контроль над размером BlockingQueue. Это не безгранично в отличие от более ранних двух вариантов. Я не выйду из памяти из-за огромного скопления отложенных задач Callable/Runnable в неожиданной турбулентности в системе.
Вы можете реализовать собственную политику обработки отклонений ИЛИ использовать одну из политик:
В стандартном ThreadPoolExecutor.AbortPolicy
обработчик генерирует исключительную ситуацию RejectedExecutionException при отклонении.
В ThreadPoolExecutor.CallerRunsPolicy
поток, который вызывает выполнение, запускает задачу. Это обеспечивает простой механизм управления с обратной связью, который замедляет скорость отправки новых задач.
В ThreadPoolExecutor.DiscardPolicy
задача, которая не может быть выполнена, просто отбрасывается.
В ThreadPoolExecutor.DiscardOldestPolicy
, если исполнитель не выключен, задача во главе рабочей очереди отбрасывается, а затем повторяется попытка выполнения (что может снова привести к сбою, что приведет к повторению).
Вы можете реализовать собственную фабрику потоков для следующих случаев использования:
Вы должны использовать newCachedThreadPool только тогда, когда у вас есть недолговечные асинхронные задачи, как указано в Javadoc, если вы отправляете задачи, которые занимают больше времени для обработки, вы в конечном итоге создаете слишком много потоков. Вы можете поразить 100% -ный процессор, если вы отправляете длинные запущенные задачи быстрее с помощью newCachedThreadPool (http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/).
Я делаю несколько быстрых тестов и получаю следующие выводы:
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:
Потоки никогда не увеличиваются от минимального размера до максимального размера, что означает, что пул потоков имеет фиксированный размер как минимальный размер.