FixedThreadPool против CachedThreadPool: меньшее из двух зол

Итак, у меня есть программа, которая порождает потоки (~5-150), которые выполняют кучу задач. Первоначально я использовал FixedThreadPool, потому что этот же вопрос предположил, что они лучше подходят для более длительных задач и с моими очень ограниченными знаниями многопоточности, я рассмотрел средний срок службы нитей (несколько минут)"долго жил".

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

испытывая их обоих предварительно, нет кажется чтобы быть разницей, поэтому я склонен идти с CachedThreadPool просто, чтобы избежать отходов. Однако означает ли продолжительность жизни потоков, что я должен вместо этого выбрать FixedThreadPool и просто иметь дело с неиспользуемыми потоками? этот вопрос кажется, что эти дополнительные потоки не впустую, но я был бы признателен за разъяснение.

3 ответов


CachedThreadPool-это именно то, что вы должны использовать для своей ситуации, поскольку нет никаких негативных последствий для использования одного для длинных потоков. Комментарий в Java doc о том, что CachedThreadPools подходит для коротких задач, просто предполагает, что они особенно подходят для таких случаев, а не то, что они не могут или не должны использоваться для задач, связанных с длительными задачами.

в дальнейшем, исполнителей.newCachedThreadPool и исполнителей.newFixedThreadPool оба поддерживаются одной и той же реализацией пула потоков (по крайней мере, в открытом JDK) только с разными параметрами. Различия просто являются их потоком минимум, максимум, поток убить время и тип очереди.

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>());
}

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

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


и FixedThreadPool и CachedThreadPool являются злом в высоконагруженных приложениях.

CachedThreadPool более опасно, чем FixedThreadPool

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

  1. неограниченный характер очереди задач: это может привести к нехватке памяти или высокой задержке
  2. длинные запущенные потоки вызовут CachedThreadPool выйти из-под контроля в потоке творение

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

  1. установите очередь задач как ограниченную очередь, чтобы иметь лучший контроль
  2. имеют право RejectionHandler - свой собственный RejectionHandler или обработчики по умолчанию с JDK
  3. если у вас есть что-то сделать до/после завершения задачи, переопределите beforeExecute(Thread, Runnable) и afterExecute(Runnable, Throwable)
  4. переопределить ThreadFactory если требуется настройка потока
  5. размер пула потоков управления динамически во время выполнения ( связанный вопрос SE:Динамический Пул Потоков)

Итак, у меня есть программа, которая порождает потоки (~5-150), которые выполняют кучу задач.

вы уверены, что понимаете, как потоки фактически обрабатываются вашей ОС и выбранным оборудованием? Как Java сопоставляет потоки с потоками ОС, как это сопоставляет потоки с потоками процессора и т. д.? Я спрашиваю, потому что создание 150 потоков внутри одной JRE имеет смысл только в том случае, если у вас есть массивные ядра/потоки CPU под ними, что, скорее всего, не так. В зависимости от ОС и ОЗУ в использование, создание более n потоков может даже привести к завершению JRE из-за ошибок OOM. Поэтому вы должны действительно различать потоки и работать с этими потоками,сколько работы вы даже можете обработать и т. д.

и это проблема с CachedThreadPool: не имеет смысла стоять в очереди на длительную работу в потоках, которые на самом деле не могут работать, потому что у вас есть только 2 ядра процессора, способные обрабатывать эти потоки. Если вы закончите с 150 запланированными потоками, вы может создать много ненужных накладных расходов для планировщиков, используемых в Java и операционной системы одновременно обрабатывать их. Это просто невозможно, если у вас есть только 2 ядра процессора, если ваши потоки не ждут ввода-вывода или такого все время. Но даже в этом случае много потоков создадут много ввода-вывода...

и эта проблема не возникает с FixedThreadPool, созданным, например, с потоками 2+n, где n является разумным низким, конечно, потому что с этим оборудованием и ресурсами ОС используются с гораздо меньшими накладными расходами для управления потоками, которые все равно не могут работать.