Душеприказчики.newCachedThreadPool() против исполнителей.newFixedThreadPool()


newCachedThreadPool() и newFixedThreadPool()

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

7 121

7 ответов:

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

newFixedThreadPool

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

newCachedThreadPool

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

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

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

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

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

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

таким образом, в сильно загруженном производственном сервере, вам гораздо лучше использовать исполнителей.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() не является отличным выбором для серверного кода, который обслуживает несколько клиентов и одновременные запросы.

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

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

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

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

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

если вы не беспокоитесь о unbounded очередь Callable / Runnable задач, вы можете использовать один из них. Как предложил Бруно, я тоже предпочитаю newFixedThreadPool to newCachedThreadPool между этими двумя.

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

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

плюсы:

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

  2. вы можете осуществить заказ неприятие обращения политика или используйте одну из политик:

    1. по умолчанию ThreadPoolExecutor.AbortPolicy, обработчик вызывает runtime RejectedExecutionException при отклонении.

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

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

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

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

    1. чтобы задать более описательное имя потока
    2. чтобы установить статус демона потока
    3. установить приоритет потока

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

Я делаю некоторые быстрые тесты и имею следующие выводы:

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

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

исключение в потоке" main " java.утиль.параллельный.RejectedExecutionException: задача java.утиль.параллельный.FutureTask@3fee733d отклонен от java.утиль.параллельный.ThreadPoolExecutor@5acf9800[работает, размер пула = 3, активные потоки = 3, в очереди задачи = 0, выполненные задачи = 0]

на java.утиль.параллельный.ThreadPoolExecutor$AbortPolicy.rejectedExecution (ThreadPoolExecutor.java: 2047)

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

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