← Назад к вопросам
В чем разница между коллекциями Queue, Set и List?
1.0 Junior🔥 211 комментариев
#Коллекции#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Многопоточность в Java: Thread, Runnable и ExecutorService
Многопоточность - критическая тема для любого Java разработчика. Это позволяет выполнять несколько операций одновременно, повышая производительность приложения.
Основные концепции
Thread vs Runnable
Оба способа создать поток, но есть отличия:
// Способ 1: наследование от Thread
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Выполняется в отдельном потоке");
}
}
// Использование
MyThread thread = new MyThread();
thread.start(); // start(), не run()!
// Способ 2: реализация Runnable (предпочтительно)
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Выполняется в отдельном потоке");
}
}
// Использование
Thread thread = new Thread(new MyRunnable());
thread.start();
// Или через lambda (Java 8+)
Thread thread = new Thread(() -> {
System.out.println("Выполняется в отдельном потоке");
});
thread.start();
Почему Runnable лучше:
- Java не поддерживает множественное наследование
- С Runnable можешь наследоваться от другого класса
- Более гибкий подход
- Разделение ответственности
Thread lifecycle (жизненный цикл)
NEW → RUNNABLE → RUNNING → BLOCKED/WAITING → TERMINATED
public class ThreadLifecycle {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("Поток выполняется");
try {
Thread.sleep(2000); // WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("State: " + thread.getState()); // NEW
thread.start(); // Переводит в RUNNABLE
Thread.sleep(1000);
System.out.println("State: " + thread.getState()); // TIMED_WAITING
thread.join(); // Ждем завершения
System.out.println("State: " + thread.getState()); // TERMINATED
}
}
Проблемы многопоточности
1. Race Condition (Состояние гонки)
// ПЛОХО: Race condition
public class Counter {
private int count = 0; // shared resource
public void increment() {
count++; // 3 операции: read, increment, write
// Между ними может вмешаться другой поток!
}
}
// 2 потока выполняют increment() одновременно
// count может быть 1 вместо 2
// ХОРОШО: используй synchronized
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // Теперь безопасно
}
}
2. DeadLock (Взаимная блокировка)
// ПЛОХО: DeadLock
public class Account {
private int balance;
public synchronized void transfer(Account other, int amount) {
this.balance -= amount;
other.deposit(amount); // Вызывает synchronized метод!
}
public synchronized void deposit(int amount) {
this.balance += amount;
}
}
// Два потока:
// Поток 1: account1.transfer(account2, 100);
// Поток 2: account2.transfer(account1, 100);
// Deadlock!
// ХОРОШО: используй Lock или правильный порядок
public class Account {
private int balance;
private final Object lock = new Object();
public void transfer(Account other, int amount) {
// Всегда блокируем в одном порядке (по ID)
Account first = this.id < other.id ? this : other;
Account second = this.id < other.id ? other : this;
synchronized (first) {
synchronized (second) {
this.balance -= amount;
other.balance += amount;
}
}
}
}
ExecutorService (современный подход)
Это рекомендуемый способ управления потоками:
// Создание
ExecutorService executor = Executors.newFixedThreadPool(5);
// Запуск задач
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Результат";
});
// Получение результата
try {
String result = future.get(); // Блокирует до готовности
String result = future.get(2, TimeUnit.SECONDS); // С timeout
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
// Корректное завершение
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
Типы ExecutorService
// Fixed Thread Pool (рекомендуется)
ExecutorService executor = Executors.newFixedThreadPool(10);
// Cached Thread Pool (для коротких задач)
ExecutorService executor = Executors.newCachedThreadPool();
// Single Thread Executor (один поток)
ExecutorService executor = Executors.newSingleThreadExecutor();
// Scheduled Executor (периодические задачи)
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
executor.schedule(() -> {
System.out.println("Запустится через 2 секунды");
}, 2, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(() -> {
System.out.println("Каждые 5 секунд");
}, 0, 5, TimeUnit.SECONDS);
Синхронизация
synchronized
// На методе
public synchronized void method() { }
// На блоке кода
synchronized (lock) {
// Критическая секция
}
volatile
// Для флагов
private volatile boolean running = true;
// Гарантирует видимость изменений между потоками
Atomic классы
private AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // Безопасная инкрементация
int value = counter.get();
Lock интерфейс
private final Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// Критическая секция
} finally {
lock.unlock(); // ВАЖНО: в finally!
}
}
CountDownLatch и CyclicBarrier
// CountDownLatch: ждем N событий
CountDownLatch latch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
// Работа
latch.countDown();
});
}
latch.await(); // Ждем пока все завершат
// CyclicBarrier: все потоки ждут друг друга
CyclicBarrier barrier = new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
// Подготовка
barrier.await(); // Все ждут
// Синхронизированное выполнение
});
}
Лучшие практики
- Используй ExecutorService вместо создания потоков вручную
- Всегда вызывай shutdown() после использования
- Используй volatile для флагов
- Избегай synchronized(this) - используй отдельные lock объекты
- Minimизируй критические секции - чем меньше, тем лучше
- Избегай nested locks - риск deadlock
- Используй ConcurrentHashMap вместо synchronizedMap
- Профилируй и тестируй многопоточный код
Пример: Потокобезопасный кэш
public class ThreadSafeCache<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
public V get(K key, Callable<V> loader) throws Exception {
return cache.computeIfAbsent(key, k -> {
try {
return loader.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
Многопоточность - мощный инструмент, но требует осторожности. Правильное использование может сильно улучшить производительность приложения.