1. Що таке потік виконання (thread)
Потік як окрема лінія виконання
У Java (та в програмуванні загалом) потік виконання — це незалежна послідовність команд, яка йде паралельно з іншими потоками всередині однієї програми. Уявіть фабрику: у кожної швачки — свій робочий стіл і завдання; вона працює незалежно, але разом вони дають спільний результат.
За замовчуванням Java‑програма запускається з одного потоку — того, що запускає метод main. Але ніщо не заважає нам створювати додаткові потоки, щоб різні частини програми виконувалися одночасно.
Процеси й потоки: у чому різниця?
- Процес — це «важковагова» одиниція виконання. У кожного процесу своя область пам’яті, свої змінні, свої ресурси. Процеси повністю ізольовані один від одного — якщо один «зламається», інші не постраждають.
- Потік (thread) — це «легковагова» одиниця виконання всередині процесу. Усі потоки одного процесу спільно використовують пам’ять і ресурси. Це означає, що вони можуть легко обмінюватися даними (і, на жаль, так само легко заважати один одному).
Аналогія:
Процес — це окрема квартира: у кожної свої стіни та мешканці.
Потоки — це мешканці всередині однієї квартири: у кожного свої справи, але кухня й ванна спільні.
Як це виглядає в Java?
Коли ви запускаєте програму, JVM створює щонайменше один потік — головний (main). Але ви можете створювати нові потоки, щоб виконувати завдання паралельно.
2. Навіщо потрібна багатопотоковість
Реактивність: UI не повинен «підвисати»
Припустімо, ви пишете графічну програму — наприклад, текстовий редактор. Користувач натиснув кнопку «Зберегти», а ви пішли довго й нудно записувати файл на диск. Якщо все це робити в головному потоці, вікно програми «зависне»: користувач не зможе нічого натиснути, курсор не рухається, інтерфейс не реагує. Якщо ж зберігати файл в окремому потоці — інтерфейс залишиться чутливим, і користувач навіть зможе передумати та закрити програму.
Життєвий приклад:
Ви відкрили браузер і почали завантажувати великий файл. Якби браузер не використовував потоки, ви б не змогли ні відкрити нову вкладку, ні прокрутити сторінку, поки файл не завантажиться!
Паралельна обробка даних
Припустімо, у вас є список із тисячі файлів, які потрібно обробити (наприклад, перерахувати хеші або замінити текст). Чому б не обробити їх паралельно? Кожен потік бере свій файл і працює з ним незалежно, тож вся робота завершується в кілька разів швидше.
Приклад:
Сервер обробляє запити від сотень клієнтів. Якби сервер робив це в одному потоці, решта клієнтів чекали б своєї черги вічно. А з потоками кожен запит обробляється незалежно!
Використання багатоядерних процесорів
Сучасні процесори — це не один «мозок», а ціла команда (ядра), які можуть працювати паралельно. Якщо ваша програма використовує лише один потік, інші ядра нудьгують і грають у сапера. Якщо ж ви запускаєте кілька потоків — усі ядра зайняті справою, а програма виконується швидше.
Цікавий факт:
Навіть ваш телефон має кілька ядер, а ноутбуки й сервери — десятки! Не використовувати їх усі — все одно що купити автобус і їздити на ньому наодинці.
3. Приклади з життя
| Сфера | Приклад багатопотоковості |
|---|---|
| Завантаження файлів | Завантаження кількох файлів одночасно |
| Користувацький UI | Застосунок не «зависає» під час завантаження/збереження даних |
| Сервери | Обробка багатьох мережевих запитів паралельно |
| Ігри | Окремі потоки для фізики, графіки, музики, AI |
| Месенджери | Отримання повідомлень, надсилання файлів, оновлення інтерфейсу |
| Обробка відео | Обробка кадрів паралельно |
Міні-аналогія:
Кухар готує суп, а паралельно духовка пече пиріг, а робот-пилосос прибирає підлогу — усе це відбувається одночасно, і вечеря готується швидше!
4. Потенційні складнощі багатопотоковості
Стан гонки (race condition)
Коли кілька потоків одночасно змінюють одну й ту саму змінну, результат може бути непередбачуваним. Наприклад, якщо два потоки одночасно збільшують спільний лічильник, підсумкове значення може бути некоректним. Про це докладніше поговоримо в одній із наступних лекцій.
Синхронізація
Щоб потоки не заважали один одному, доводиться вигадувати способи «домовитися» — хто й коли може змінювати дані. Це називається синхронізація. Для цього є спеціальні ключові слова й конструкції (synchronized, блокування тощо), про які ми поговоримо пізніше.
Deadlock (взаємне блокування)
Іноді потоки можуть так «подружитися», що чекатимуть один одного вічно, і програма зависне. Це називається deadlock — і це одна з найпідступніших помилок у багатопотоковому програмуванні.
Налагодження та тестування
Помилки в багатопотокових програмах дуже складно виловити: іноді все працює, іноді — ні. Іноді помилка проявляється лише на сервері або в користувача, а у вас на комп’ютері все ідеально. Це робить тестування та налагодження багатопотокового коду справжнім квестом для розробника.
5. Короткий огляд: як виглядає багатопотокова програма
Приклад без потоків:
public class Main {
public static void main(String[] args) {
// Рахуємо до 5
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
// Виводимо літери
for (char c = 'A'; c <= 'E'; c++) {
System.out.println(c);
}
}
}
Виведення завжди однакове:
1
2
3
4
5
A
B
C
D
E
Приклад із потоками:
public class Main {
public static void main(String[] args) {
Thread numbers = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println(i);
try {
Thread.sleep(100); // Трохи зачекаємо
} catch (InterruptedException e) {
// Ігноруємо
}
}
});
Thread letters = new Thread(() -> {
for (char c = 'A'; c <= 'E'; c++) {
System.out.println(c);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ігноруємо
}
}
});
numbers.start();
letters.start();
}
}
Виведення буде перемішаним:
1
A
2
B
3
C
4
D
5
E
або, якщо потоки «змагаються», порядок може бути іншим. Головне — обидва цикли виконуються паралельно!
6. Корисні нюанси
Візуальна схема: як потоки працюють разом
+-------------------+ +-------------------+
| Головний потік | | Другий потік |
+-------------------+ +-------------------+
| 1 | 2 | 3 | 4 | 5 | | A | B | C | D | E |
+-------------------+ +-------------------+
| |
| Обидва працюють |
| одночасно |
+-------------------------+
Де Java використовує потоки «під капотом»
- Збирання сміття (Garbage Collector) — окремий потік очищає невикористовувані об’єкти.
- Потоки введення-виведення (IO) — читання й запис файлів, мережеві з’єднання.
- Сервери та веб-застосунки — кожен клієнтський запит обробляється в окремому потоці.
- Таймери, планувальники завдань — виконання завдань за розкладом.
7. Типові помилки новачків
Помилка № 1: Очікування, що потоки завжди прискорюють програму.
Насправді, якщо у вас однопроцесорна машина або ви неправильно організували роботу, багатопотоковість може лише сповільнити виконання через «плутанину» та накладні витрати на перемикання між потоками.
Помилка № 2: Ігнорування проблем синхронізації.
Багато хто впевнений: «Я ж просто запускаю два потоки, що тут може піти не так?» Але якщо обидва потоки змінюють одну змінну, результат може бути цілком неочікуваним.
Помилка № 3: Використання потоків для всього підряд.
Не варто запускати окремий потік для кожної дрібниці. Потоки — це ресурс, і їхня надмірна кількість може призвести до значних уповільнень і навіть до збою програми.
Помилка № 4: Відсутність обробки помилок.
Потоки можуть викидати винятки (наприклад, під час роботи з файлами або мережею). Якщо не обробляти ці помилки, програма може аварійно завершитися або «зависнути».
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ