Ответ
В 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. Он решает проблемы производительности (переиспользование потоков) и упрощает управление жизненным циклом.