2011-05-16 2 views
8

Пол Tyma presentation имеет следующую строку:Почему ExecutorService создан через newCachedThreadPool зло?

Executors.newCacheThreadPool зло, умирают умирают умирают

Почему это зло?

Я буду опасаться догадки: это потому, что количество потоков будет расти неограниченным образом. Таким образом, сервер, который был slashdotted, вероятно, погибнет, если будет достигнуто максимальное количество потоков JVM?

+1

Я использовал пул кешей, который так сильно повредил мою систему Windows Vista, что мне пришлось задействовать его. Менеджер задач не ответил. –

+0

пришел сюда по той же причине :) – Eugen

ответ

8

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

4

Есть пара проблем с ним. Неограниченный рост с точки зрения потоков является очевидной проблемой - если у вас есть задачи с привязкой к процессору, то для того, чтобы запускать их гораздо больше, чем доступные процессоры, просто собирается создавать накладные расходы планировщика, когда ваш контекст потоков переключается повсюду, и ни один из них практически не продвигается. Если ваши задачи связаны с IO, но все становится более тонким. Знать, как масштабировать пулы потоков, которые ждут в сети или файле IO, намного сложнее и сильно зависит от латентности этих событий ввода-вывода. Более высокие задержки означают, что вам нужно (и может поддерживать) больше потоков.

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

Другая большая проблема с пулом кэшированных потоков заключается в том, что он может быть медленным для потока производителей задач. Пул настроен с помощью SynchronousQueue для задач, которые будут предложены. Эта реализация очереди в основном имеет нулевой размер и работает только при наличии подходящего потребителя для производителя (есть опрос потоков, когда другой предлагает). Фактическая реализация была значительно улучшена в Java6, но она по-прежнему сравнительно медленна для производителя, особенно когда она терпит неудачу (поскольку производитель затем отвечает за создание нового потока для добавления в пул). Часто для потолка производителя лучше всего просто отбросить задачу в реальной очереди и продолжить.

Проблема заключается в том, что у кого-то нет пула с небольшим набором основных потоков, который, когда они все заняты, создает новые потоки до некоторого макс и затем ставит в очередь очередные задачи. Фиксированные пулы потоков, похоже, обещают это, но они только начинают добавлять больше потоков, когда основная очередь отклоняет больше задач (она заполнена). A LinkedBlockingQueue никогда не заполняется, поэтому эти пулы никогда не растут за пределами основного размера. ArrayBlockingQueue имеет емкость, но по мере того, как он только увеличивает пул при достижении пропускной способности, это не уменьшает скорость производства, пока не станет большой проблемой. В настоящее время решение требует использования хорошего rejected execution policy, такого как запуск звонков, но он нуждается в некоторой осторожности.

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

16

(Это Павел)

Намерения слайда было (помимо наличия шуточных формулировок), что, как вы говорите, что пул потоков растет неограниченно создавать новые темы.

Пул потоков по сути представляет собой очередь и точку передачи работы внутри системы. То есть что-то подпитывает его работу (и это может быть кормление в другом месте). Если пул потоков начинает расти, потому что он не может идти в ногу со спросом.

В целом, это нормально, поскольку компьютерные ресурсы конечны и что очередь построена для обработки всплесков работы. Однако этот пул потоков не дает вам контроля над тем, чтобы выталкивать узкое место вперед.

Например, в сценарии сервера несколько потоков могут принимать в сокетах и ​​передавать пул потоков клиентам для обработки. Если этот пул потоков начинает выходить из-под контроля - система должна прекратить прием новых клиентов (фактически, потоки «акцептора» затем часто переходят в пул потоков временно, чтобы помочь обрабатывать клиентов).

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

IIRC, семенные серверы SEDA Мэтта Уэлша (которые являются асинхронными) создали пулы потоков, которые изменили свой размер в соответствии с характеристиками сервера.

Идея прекратить прием новых клиентов звучит плохо, пока вы не поймете, что альтернатива - это искалеченная система, которая не обрабатывает клиентов. (Опять же, при понимании, что компьютеры конечны - даже оптимально настроенная система имеет предел)

Кстати, JVM ограничивают потоки нитями 16k (обычно) или 32k в зависимости от JVM. Но если вы связаны с ЦП, этот предел не очень уместен - запуск другого потока в связанной с CPU системе является контрпродуктивным.

Я с радостью запускаю системы на 4 или 5 тысяч потоков. Но, приближаясь к пределу 16k, все имеет тенденцию бояться (этот предел JVM принудительно - у нас было еще много потоков в Linux C++), даже если не связано с ЦП.

+0

Учитывая, что это автор оригинальной цитаты, и он предоставил наиболее полный ответ, я считаю, что это должен быть принятый ответ. – KomodoDave

Смежные вопросы