FixedThreadPool vs CachedThreadPool: менша з двох зол


93

У мене є програма, яка породжує теми (~ 5-150), які виконують купу завдань. Спочатку я використовував те, FixedThreadPoolтому що подібне запитання припускало, що вони краще підходять для більш тривалих завдань, і з моїх дуже обмежених знань багатопотокової роботи я вважав середню тривалість життя ниток (кілька хвилин) " довго прожили ".

Однак я нещодавно додав можливість нерестувати додаткові нитки, і це робить мене вище встановленого я межі потоку. У цьому випадку було б краще вгадати та збільшити кількість потоків, які я можу дозволити, або перейти на CachedThreadPoolтак, щоб у мене не було марних потоків?

Спробувавши їх обох попередньо, схоже , немає різниці, тому я схильний їхати з тим, CachedThreadPoolщоб уникнути відходів. Однак чи означає тривалість життя ниток означає, що я повинен замість цього обрати FixedThreadPoolі просто мати справу з невикористаними нитками? Це запитання робить таке, що ці зайві теми не витрачаються даремно, але я вдячний за уточнення.

Відповіді:


108

CachedThreadPool - саме те, що ви повинні використовувати для своєї ситуації, оскільки немає негативних наслідків для використання одного для довгих потокових потоків. Коментар у java doc про те, що CachedThreadPools є придатним для коротких завдань, просто підказує, що вони особливо підходять для таких випадків, а не те, що вони не можуть або не повинні використовуватися для завдань, що передбачають тривалі завдання.

Для подальшої деталізації Executors.newCchedThreadPool і Executors.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 не обмежить їх для вас, тому вам може знадобитися написати власний код, щоб переконатися, що ви не запускаєте занадто багато потоків. Це дійсно залежить від дизайну вашої заявки та способів подання завдань до служби виконавця.


1
"CachedThreadPool - саме те, що ви повинні використовувати для своєї ситуації, оскільки немає негативних наслідків для використання одного для довгих потокових потоків". Я не думаю, що я згоден. CachedThreadPool динамічно створює нитки без верхньої межі. Тривалі завдання, що виконуються на великій кількості потоків, можуть потенційно заграти всі ресурси. Крім того, наявність більшої кількості потоків, ніж ідеальна, може призвести до надмірного витрачання ресурсів на зміну контексту цих потоків. Хоча ви пояснили наприкінці відповіді, що потрібне замовлення на замовлення, початок відповіді є дещо оманливим.
Нішит

1
Чому б просто не створити обмежений на ThreadPoolExecutorзразок ThreadPoolExecutor(0, maximumPoolSize, 60L, TimeUnit.SECONDS, SynchronousQueue())?
Abhijit Sarkar

45

І те, FixedThreadPoolі CachedThreadPoolінше - це зло у високонавантажених додатках.

CachedThreadPool небезпечніше ніж FixedThreadPool

Якщо ваша програма сильно завантажена і вимагає низької затримки, краще позбутися обох варіантів через менші недоліки

  1. Необмежений характер черги завдань: це може спричинити відсутність пам’яті або високу затримку
  2. Довгі запущені нитки CachedThreadPoolвийдуть з-під контролю над створенням теми

Оскільки ви знаєте, що обидва - це зло, менше зло не приносить добра. Віддайте перевагу ThreadPoolExecutor , який забезпечує детальний контроль за багатьма параметрами.

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

Що робити, якщо хтось вирішить використовувати commonPool?
Crosk Cool

1
@Ravindra - Ви прекрасно пояснили мінуси і CachedThreadPool, і FixedThreadPool. Це показує, що ви глибоко розумієте пакет пакетів.
Айяскант

5

Отже, у мене є програма, яка породжує теми (~ 5-150), які виконують купу завдань.

Ви впевнені, що розумієте, як нитки насправді обробляються вашою ОС та обладнанням, яке ви вибрали? Як Java відображає потоки в потоки ОС, як це відображає потоки в потоки процесора тощо? Я запитую, тому що створювати 150 потоків всередині ONE JRE має сенс лише якщо у вас є масивні CPU ядра / потоки під ними, що, швидше за все, не так. Залежно від ОС та оперативної пам’яті, що використовуються, створення більш ніж n потоків може навіть призвести до припинення вашого JRE через помилки OOM. Тож вам слід справді розрізняти нитки та роботу, яку виконувати ці нитки, скільки робіт ви навіть можете обробити тощо.

І в цьому проблема CachedThreadPool: Не має сенсу чергувати тривалі роботи в потоках, які насправді не можуть запускатись, оскільки у вас є лише 2 CPU ядра, здатні обробляти ці потоки. Якщо ви закінчите 150 запланованих потоків, ви можете створити багато зайвих накладних витрат для планувальників, які використовуються в Java та ОС для їх одночасної обробки. Це просто неможливо, якщо у вас є лише 2 ядра процесора, якщо тільки ваші потоки постійно не чекають вводу-виводу або такого. Але навіть у такому випадку багато ниток створило б багато вводу-виводу ...

І ця проблема не виникає з FixedThreadPool, створеним, наприклад, з 2 + n потоками, де n, звичайно, низький, звичайно, тому що з цим обладнанням та ресурсами ОС використовується набагато менше накладних витрат для управління потоками, які ніяк не можуть працювати.


Іноді немає кращого вибору, ви можете просто мати 1 ядро ​​процесора, але якщо ви використовуєте сервер, де кожен запит користувача викликав би потік для обробки запиту, іншого розумного вибору не буде, особливо якщо ви плануєте щоб масштабувати сервер, коли ви наростите базу користувачів.
Мішель Файнштейн

@mFeinstein Як не можна вибрати, якщо хтось може обрати реалізацію пулу потоків? У вашому прикладі з 1 ядром процесора лише поява більше потоків просто не має сенсу, він ідеально підходить для мого прикладу за допомогою FixedThreadPool. Це також легко масштабується, спочатку на або двох робочих нитках, пізніше з 10 або 15 залежно від кількості ядер.
Торстен Шьонінг

2
Переважна більшість реалізацій веб-серверів створить один новий потік для кожного нового HTTP-запиту ... Їм буде байдуже, скільки фактичних ядер має машина, це робить реалізацію більш простою та легшою для масштабування. Це стосується багатьох інших конструкцій, де ви просто хочете один раз кодувати та розгортати, і вам не доведеться перекомпілювати та передислокувати, якщо ви зміните машину, яка може бути хмарним екземпляром.
Мішель Файнштейн

@mFeinstein Більшість веб-серверів використовують пули потоків для запитів самостійно, просто тому, що нерестові потоки, які не можуть запускатися, не мають сенсу, або вони використовують петлі подій для з'єднань і обробляють запити в пулах після цього чи інше. Крім того, вам не вистачає суті, а саме, що питання полягає в тому, щоб хтось міг вибрати правильний пул потоків, а нерестові потоки, які так чи інакше не можуть запускатися, все одно не мають сенсу. FixedthreadPool налаштований на розумну кількість ниток на машині залежно від масштабів ядер.
Торстен Шьонінг

3
@ ThorstenSchöning, що на 2-ядерній машині 50 потоків, пов'язаних з процесором, не допомагає. Наявність 50 потоків, пов’язаних з введенням-виводом, на двоядерній машині може бути дуже корисною.
Пол Дрейпер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.