Многопоточность в Java: эффективное параллельное программирование
Введение в многопоточность
Java — это многопоточный язык программирования, позволяющий разрабатывать приложения, где несколько потоков выполняются параллельно. Каждый поток — это легковесный процесс с собственным стеком вызовов, но с общей областью памяти (кучей). Это обеспечивает эффективное использование многоядерных процессоров.
Многозадачность на уровне ОС распределяет процессорное время между процессами. Многопоточность переносит эту идею внутрь одного приложения: потоки внутри процесса делят ресурсы (память, файловые дескрипторы), но выполняются независимо.
Жизненный цикл потока
Поток проходит через несколько состояний:
- NEW (Новый) — поток создан, но не запущен (
new Thread()). - RUNNABLE (Выполняемый) — после вызова
start()поток готов к выполнению или уже выполняется. - BLOCKED (Заблокирован) — ожидает монитор для входа в синхронизированный блок/метод.
- WAITING (Ожидание) — бесконечно ждет уведомления от другого потока (
Object.wait(),Thread.join()). - TIMED_WAITING (Ожидание с таймаутом) — ждет в течение заданного времени (
Thread.sleep(),wait(timeout)). - TERMINATED (Завершен) — выполнение метода
run()завершилось нормально или с исключением.

Приоритеты потоков
У каждого потока есть приоритет от MIN_PRIORITY (1) до MAX_PRIORITY (10). По умолчанию — NORM_PRIORITY (5). Планировщик ОС предпочитает потоки с более высоким приоритетом, но гарантий порядка выполнения нет — это зависит от платформы.
thread.setPriority(Thread.MAX_PRIORITY);
Создание потоков: два подхода
1. Реализация интерфейса Runnable (рекомендуется)
Разделяет задачу (логику) и механизм выполнения (поток). Позволяет наследоваться от другого класса.
class Task implements Runnable {
private String name;
Task(String name) { this.name = name; }
@Override
public void run() {
System.out.println("Запуск " + name);
try {
for (int i = 3; i > 0; i--) {
System.out.println(name + ": " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // восстанавливаем флаг прерывания
System.out.println(name + " прерван");
}
System.out.println(name + " завершен");
}
}
public class Demo {
public static void main(String[] args) {
new Thread(new Task("Поток-1")).start();
new Thread(new Task("Поток-2")).start();
}
}
2. Наследование от класса Thread
Проще для простых случаев, но ограничивает наследование (Java не поддерживает множественное наследование классов).
class MyThread extends Thread {
@Override
public void run() {
// логика потока
}
}
// Использование:
new MyThread().start();
Основные методы класса Thread
| Метод | Описание |
|---|---|
start() | Запускает поток (вызывает run() в новом потоке). Никогда не вызывайте run() напрямую! |
run() | Точка входа в выполнение потока. |
sleep(long ms) | Статический метод: приостанавливает текущий поток на указанное время. |
join() / join(long ms) | Ожидает завершения потока, на котором вызван. |
interrupt() | Прерывает поток (устанавливает флаг прерывания). |
isAlive() | Возвращает true, если поток запущен и не завершен. |
yield() | Статический: подсказывает планировщику уступить процессор другим потокам того же приоритета. |
currentThread() | Статический: возвращает ссылку на текущий выполняющийся поток. |
setDaemon(boolean) | Делает поток демоном (не препятствует завершению JVM). |
Продвинутые темы многопоточности
Для профессиональной разработки необходимо освоить:
- Синхронизацию —
synchronized,Lock,ReentrantLock,ReadWriteLock. - Взаимодействие потоков —
wait(),notify(),notifyAll(),Condition. - Проблемы параллелизма — дедлоки (взаимные блокировки), лайвлоки, состояние гонки (race conditions).
- Потокобезопасные коллекции —
ConcurrentHashMap,CopyOnWriteArrayList, блокирующие очереди (BlockingQueue). - Executor Framework — пулы потоков (
ExecutorService),Future,CompletableFuture. - Fork/Join Framework — для рекурсивных задач (разделяй и властвуй).
- ThreadLocal — переменные, локальные для каждого потока.
- volatile и
atomicпеременные — для безопасной публикации изменений.
Пример с ExecutorService (современный подход)
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Задача " + taskId + " выполняется в " + Thread.currentThread().getName());
try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
return "Результат " + taskId;
});
}
executor.shutdown();
try { executor.awaitTermination(1, TimeUnit.MINUTES); }
catch (InterruptedException e) { e.printStackTrace(); }
Заключение
Многопоточность в Java — мощный инструмент, но требует глубокого понимания проблем синхронизации и управления памятью. Современные приложения предпочитают высокоуровневые абстракции (ExecutorService, CompletableFuture, параллельные стримы) ручному управлению потоками.
java
- Освоение операторов Java: типы, синтаксис и практические примеры
- Комментарии в Java: типы, использование и лучшие практики
- Освоение улучшенного цикла for в Java: синтаксис, примеры и лучшие практики
- Освоение строк в Java: создание, методы и лучшие практики
- Интерфейсы в Java: концепции, реализация и лучшие практики
- Анонимные внутренние классы в Java: определение, синтаксис и практические примеры
- Try-with-resources в Java: автоматическое управление ресурсами
- Аннотации в Java: типы, размещение и практические примеры
- Assertions в Java: практическое руководство по обнаружению багов и обеспечению надежности кода
- Понимание Java Vector: использование, особенности и сравнение с ArrayList