ThreadPoolExecutor пул ниток - 1

— Пересічний програміст рано чи пізно стикається з тим, що має багато маленьких завдань, які потрібно виконувати час від часу.

Якщо ти пишеш гру, то це дії, які виконують окремі персонажі.

Якщо пишеш веб-сервер, то це різні команди, що надходять від користувачів: завантажити фото, перекодувати його в потрібний формат, застосувати потрібний шаблон і т.д.

Всі великі завдання рано чи пізно розбиваються на набір маленьких та зручних завдань.

Ось так на цьому тлі непомітно і виникає питання – а як ними керувати всіма? Якщо за хвилину потрібно виконати кілька сотень завдань? Створювати для кожного завдання свою нитку не дуже раціонально. Для кожної нитки Java-машина виділяє чимало ресурсів.

Іншими словами – створення і знищення нитки, що відпрацювала, може витрачати більше ресурсів і часу, ніж саме виконуване завдання.

Java-розробники вигадали елегантне вирішення цієї проблеми — ThreadPoolExecutor.

ThreadPoolExecutor – це клас, який має всередині дві речі:

А) Черга завдань, до яких можна додавати завдання, в міру їх появи в програмі.

Б) Пул-ниток (група ниток) – які ці завдання виконують.

При цьому нитки не знищуються після виконання завдання, а засипають. Щоб розпочати виконання нового завдання, як тільки воно з'явиться.

При створенні ThreadPoolExecutor, можна задати максимальну кількість ниток, які будуть створені та максимальну кількість завдань, які можна помістити в чергу. Тобто. можна обмежити кількість ниток числом 10, наприклад, а кількість завдань у черзі – 100.

Як працює ThreadPoolExecutor:

1) При додаванні нового завдання воно поміщається в кінець черги.

2) Якщо черга заповнена буде викинуто виняток.

3) Кожна нитка після виконання завдання бере чергове завдання з черги і починає виконувати його.

4) Якщо завдань у черзі немає, нитка засипає до їх додавання.

Підхід з обмеженням кількості ниток, що працюють, вигідний тим, що чим більше ниток, тим сильніше вони один одному заважають. Набагато ефективніше мати 5-10 ниток-виконавців і довгу чергу завдань, ніж створити 100 ниток для групи завдань, що раптово з'явилася, які конкуруватиме один з одним за ресурси: пам'ять, час процесора, доступ до бази тощо.

Приклад такої роботиThreadPoolExecutor:
<

Приклад
ExecutorService service = Executors.newFixedThreadPool(2 );

for(int i = 0; i < 10; i++)
{
 service.submit(new Runnable() {
    public void run()
    {
     // Тут ми завантажуємо щось важке з Інтернету.
    }
 });
}

— Я щось не бачу його…

— Об'єкт ThreadPoolExecutor створюється при викликі методу newFixedThreadPool.

Так ось працює він дуже просто. Як тільки ти додаєш йому завдання за допомогою методу submit, він:

А) Будить сплячу нитку для її виконання, якщо така є.

Б) Створює нову нитку для виконання завдання, якщо її немає.

В) Якщо досягнуто максимум ниток, то просто кладе завдання в кінець черги.

Я спеціально написала у прикладі – тут ми завантажуємо щось важке з інтернету. Якщо у нас є 100 завдань «завантажити щось велике з інтернету», то немає сенсу запускати багато таких завдань одночасно – ми впораємося в обмеження ширини інтернет-каналу. У такому випадку пари ниток має бути достатньо. Саме це ти й бачиш у прикладі вище:

ExecutorService service = Executors.newFixedThreadPool(2);

— Виявляється, працювати з купою завдань не так вже й складно.

— Так. Навіть легше, ніж ти можеш собі уявити. Але про це тобі розповість Кім.