Какие способы создания потоков (Threads) существуют в Java?

«Какие способы создания потоков (Threads) существуют в Java?» — вопрос из категории Java Core, который задают на 25% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Java есть несколько основных способов создания и запуска потоков, от классических до современных best practices.

1. Наследование от класса Thread Наименее гибкий способ, так как Java не поддерживает множественное наследование.

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}
// Запуск
MyThread thread = new MyThread();
thread.start(); // Не вызывайте run() напрямую!

2. Реализация интерфейса Runnable Более предпочтителен, так как отделяет задачу (Runnable) от механизма выполнения (Thread).

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running");
    }
}
// Запуск
Thread thread = new Thread(new MyRunnable());
thread.start();

3. Использование лямбда-выражений (Java 8+) Краткая запись для Runnable, так как это функциональный интерфейс.

Thread thread = new Thread(() -> {
    System.out.println("Lambda thread running");
});
thread.start();

4. Использование ExecutorService (наиболее предпочтительно) Фреймворк java.util.concurrent управляет пулом потоков, что эффективнее ручного создания.

ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
    System.out.println("Task executed by thread pool");
});
executor.shutdown(); // Важно для освобождения ресурсов

5. Использование Callable и Future Для задач, которые должны возвращать результат или бросать проверяемые исключения.

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    // Долгая задача
    return 42;
});
Integer result = future.get(); // Блокирует поток, пока результат не готов

Рекомендация: Для производственного кода всегда используйте ExecutorService. Он решает проблемы производительности (переиспользование потоков) и упрощает управление жизненным циклом.