FixedThreadPool против CachedThreadPool: меньшее из двух зол
Итак, у меня есть программа, которая порождает потоки (~5-150), которые выполняют кучу задач. Первоначально я использовал FixedThreadPool поскольку этот аналогичный вопрос предполагал, что они лучше подходят для более длительных задач и с моими очень ограниченными знаниями многопоточности, я рассмотрел средний срок службы потоков (несколько минут)"долго жил".
однако, я недавно добавил возможность порождать дополнительные потоки и делать это берет меня выше предела потока я установил. В в этом случае, было бы лучше угадать и увеличить количество потоков, которые я могу разрешить или переключиться на CachedThreadPool Так что у меня нет напрасно потраченных потоков?
пробовать их обоих предварительно, нет кажется быть разница, так что я склонен идти с CachedThreadPool просто, чтобы избежать отходов. Однако, означает ли продолжительность жизни потоков, что я должен вместо этого выбрать FixedThreadPool и просто разобраться с неиспользуемыми потоками? Этот вопрос заставляет вас думать, что эти дополнительные потоки не являются впустую, но я был бы признателен за разъяснение.
3 ответов:
CachedThreadPool-это именно то, что вы должны использовать для своей ситуации, поскольку нет никаких негативных последствий для использования одного для длинных потоков. Комментарий в документе java о том, что 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если ваше приложение сильно загружено и требует низкой задержки, лучше избавиться от обоих вариантов из-за ниже недостатков
- неограниченный характер очереди задач : это может привести к нехватке памяти или высокой задержке
- длинные запущенные потоки вызовут
CachedThreadPoolвыйти из-под контроля в потоке творениепоскольку вы знаете, что оба являются злом, меньшее зло не делает добра. Предпочитаю ThreadPoolExecutor, что обеспечивает детальный контроль по многим параметрам.
- установите очередь задач как ограниченную очередь, чтобы иметь лучший контроль
- есть право RejectionHandler - ваш собственный RejectionHandler или обработчики по умолчанию, предоставляемые JDK
- если у вас есть что-то делать перед/после завершения задачи, переопределить
beforeExecute(Thread, Runnable)иafterExecute(Runnable, Throwable)- переопределить ThreadFactory если требуется настройка потока
- размер пула потоков управления динамически во время выполнения ( связанный вопрос SE:Динамический Пул Потоков)
Итак, у меня есть программа, которая порождает потоки (~5-150), которые выполняют кучу задач.
вы уверены, что понимаете, как потоки на самом деле обрабатываются вашей ОС и оборудованием по выбору? Как Java сопоставляет потоки с потоками ОС, как это сопоставляет потоки с потоками процессора и т. д.? Я спрашиваю, потому что создание 150 потоков внутри в одном JRE имеет смысл только в том случае, если у вас есть массивные ядра процессора/потоки внизу, что, скорее всего, не так. В зависимости от ОС и оперативной памяти в использование, создание более n потоков может даже привести к тому, что ваша JRE будет прекращена из-за ошибок OOM. Поэтому вы должны действительно различать потоки и работать с этими потоками, сколько работы вы даже можете обрабатывать и т. д.
и это проблема с CachedThreadPool: не имеет смысла ставить в очередь длительную работу в потоках, которые на самом деле не могут работать, потому что у вас есть только 2 ядра процессора, способные обрабатывать эти потоки. Если вы в конечном итоге с 150 запланированных потоков вы может создать много ненужных накладных расходов для планировщиков, используемых в Java и операционной системы одновременно обрабатывать их. Это просто невозможно, если у вас есть только 2 ядра процессора, если ваши потоки не ждут ввода-вывода или такого все время. Но даже в этом случае много потоков создало бы много ввода-вывода...
и эта проблема не возникает с FixedThreadPool, созданным, например, с 2 + n потоками, где N является разумно низким, конечно, потому что с этим используются аппаратные и операционные ресурсы с гораздо меньшими накладными расходами для управления потоками, которые не могут работать в любом случае.
Comments