← Назад к вопросам

В чем разница между коллекциями 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(); // Все ждут
        // Синхронизированное выполнение
    });
}

Лучшие практики

  1. Используй ExecutorService вместо создания потоков вручную
  2. Всегда вызывай shutdown() после использования
  3. Используй volatile для флагов
  4. Избегай synchronized(this) - используй отдельные lock объекты
  5. Minimизируй критические секции - чем меньше, тем лучше
  6. Избегай nested locks - риск deadlock
  7. Используй ConcurrentHashMap вместо synchronizedMap
  8. Профилируй и тестируй многопоточный код

Пример: Потокобезопасный кэш

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);
            }
        });
    }
}

Многопоточность - мощный инструмент, но требует осторожности. Правильное использование может сильно улучшить производительность приложения.