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

Что такое многопоточная среда?

2.0 Middle🔥 191 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое многопоточная среда?

Многопоточная среда — это одна из ключевых особенностей Java. Это позволяет одному приложению выполнять несколько операций параллельно, что критично для современных приложений.

Понятие многопоточности

Многопоточность (Multithreading) — это способность программы выполнять несколько потоков выполнения одновременно в рамках одного процесса. Каждый поток — это независимый путь выполнения кода.

Процесс (Process) — это экземпляр программы, которому операционная система выделяет ресурсы (память, файловые дескрипторы).

Поток (Thread) — это легковесный процесс внутри основного процесса. Все потоки в одном процессе делят одну и ту же память, но имеют собственный call stack.

Преимущества многопоточности

  • Отзывчивость приложения — UI остаётся отзывчивым, пока какие-то операции выполняются в фоновых потоках
  • Эффективное использование ресурсов — пока один поток ждёт I/O, другие могут продолжить работу
  • Масштабируемость — можно обработать множество параллельных запросов (например, в web-сервере)
  • Лучшая утилизация многоядерных процессоров — потоки могут выполняться на разных ядрах

Создание потоков в Java

Способ 1: Наследование от Thread

class MyThread extends Thread {
    public void run() {
        System.out.println("Поток запущен");
        for (int i = 0; i < 5; i++) {
            System.out.println("Итерация " + i);
            try {
                Thread.sleep(1000); // Пауза на 1 секунду
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // Запускаем поток
        
        System.out.println("Основной поток продолжает работу");
    }
}

Способ 2: Реализация Runnable (предпочтителен)

class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Задача выполняется в потоке: " + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyTask());
        Thread thread2 = new Thread(new MyTask());
        
        thread1.start();
        thread2.start();
    }
}

Способ 3: Lambda выражение (современный подход)

Thread thread = new Thread(() -> {
    System.out.println("Выполняется в потоке: " + Thread.currentThread().getName());
    for (int i = 0; i < 3; i++) {
        System.out.println("i = " + i);
    }
});

thread.start();

Управление потоками

public class ThreadManagement {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Поток начал работу");
                Thread.sleep(2000);
                System.out.println("Поток завершил работу");
            } catch (InterruptedException e) {
                System.out.println("Поток прерван");
            }
        });
        
        thread.setName("MyWorkerThread");
        thread.setPriority(Thread.MAX_PRIORITY); // Приоритет 1-10
        thread.start();
        
        // Ждём завершения потока
        thread.join(); // Основной поток ждёт
        
        System.out.println("Основной поток продолжил после завершения рабочего потока");
    }
}

Синхронизация и Race Conditions

Race Condition — это ошибка, когда два потока одновременно получают доступ к общему ресурсу и результат зависит от порядка выполнения.

public class Counter {
    private int count = 0;
    
    // Плохо: Race condition
    public void incrementUnsafe() {
        count++; // Это НЕ атомарная операция!
    }
    
    // Хорошо: Синхронизированный метод
    public synchronized void incrementSafe() {
        count++; // Только один поток может выполнять одновременно
    }
    
    public synchronized int getCount() {
        return count;
    }
}

Блокировка (Lock)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SharedResource {
    private int value = 0;
    private Lock lock = new ReentrantLock();
    
    public void updateValue(int newValue) {
        lock.lock();
        try {
            System.out.println("Обновляю значение с " + value + " на " + newValue);
            this.value = newValue;
        } finally {
            lock.unlock(); // ВСЕГДА разблокируем в finally
        }
    }
    
    public int getValue() {
        lock.lock();
        try {
            return value;
        } finally {
            lock.unlock();
        }
    }
}

Thread Pool и ExecutorService

Создавать новый Thread для каждой задачи — плохая идея. Вместо этого используем thread pool.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolExample {
    public static void main(String[] args) throws Exception {
        // Создаём пул из 4 потоков
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // Отправляем 10 задач
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Задача " + taskId + " выполняется в потоке: " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        executor.shutdown(); // Не принимаем новые задачи
        executor.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS);
        System.out.println("Все задачи завершены");
    }
}

Получение результата от потока

ExecutorService executor = Executors.newFixedThreadPool(2);

Future<Integer> result = executor.submit(() -> {
    // Выполняем долгую операцию
    Thread.sleep(2000);
    return 42; // Возвращаем результат
});

// Получаем результат (блокирует, если не готов)
Integer value = result.get(); // Вернёт 42
System.out.println("Результат: " + value);

Проблемы многопоточности

Deadlock — два потока ждут друг друга:

// Плохо: может привести к deadlock
lock1.lock();
lock2.lock();
// ... код
lock2.unlock();
lock1.unlock();

Лучше: Всегда захватывай блокировки в одном порядке.

Best Practices

  • Используй ExecutorService вместо создания потоков вручную
  • Избегай synchronized если возможно — используй concurrent collections (ConcurrentHashMap, CopyOnWriteArrayList)
  • Будь осторожен с shared state — минимизируй количество данных, доступных нескольким потокам
  • Используй wait/notify осторожно — лучше использовать CountDownLatch, CyclicBarrier, Semaphore
  • Всегда разблокируй ресурсы в finally или используй try-with-resources
  • Тестируй многопоточный код с нагрузкой — race conditions трудно поймать

В production коде я обычно использую Spring's TaskExecutor или reactive фреймворки (Project Reactor, RxJava) для сложных сценариев параллелизма.

Что такое многопоточная среда? | PrepBro