В чем разница между запуском интерфейса Runnable run и start?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между run() и start() в Runnable/Thread
Это одна из самых распространённых ошибок при работе с потоками в Java. Хотя оба метода существуют и вызываются похоже, их поведение совершенно разное.
Thread.start() — правильный способ
Метод start() создаёт новый поток и запускает в нём метод run().
Thread thread = new Thread(() -> {
System.out.println("Запущено в потоке: " + Thread.currentThread().getName());
});
thread.start(); // Создаёт новый поток
Вывод:
Запущено в потоке: Thread-0
Thread.run() — выполняет в текущем потоке
Метод run() выполняет код в текущем потоке (где он был вызван), не создавая новый.
Thread thread = new Thread(() -> {
System.out.println("Запущено в потоке: " + Thread.currentThread().getName());
});
thread.run(); // Выполняет в main потоке
Вывод:
Запущено в потоке: main
Визуальное сравнение
public class ThreadExample {
public static void main(String[] args) throws InterruptedException {
System.out.println("Main поток: " + Thread.currentThread().getName());
Runnable task = () -> {
for (int i = 0; i < 3; i++) {
System.out.println("Задача в " + Thread.currentThread().getName());
Thread.sleep(100);
}
};
// ПРАВИЛЬНО — новый поток
Thread t1 = new Thread(task, "Поток-1");
t1.start(); // Создаёт новый поток
// НЕПРАВИЛЬНО — выполнит в main потоке
Thread t2 = new Thread(task, "Поток-2");
t2.run(); // Выполнит в текущем потоке (main)
t1.join(); // Дождитесь завершения t1
}
}
Вывод:
Main поток: main
Задача в main // ← t2.run() выполнилась в main
Задача в main
Задача в main
Задача в Поток-1 // ← t1.start() создала новый поток
Задача в Поток-1
Задача в Поток-1
Жизненный цикл потока
Метод start() переводит поток через все состояния:
Thread thread = new Thread(() -> {
System.out.println("Выполняется");
});
// Состояние: NEW
System.out.println(thread.getState()); // NEW
// start() переводит в очередь на исполнение
thread.start();
System.out.println(thread.getState()); // RUNNABLE или RUNNING
// После выполнения
Thread.sleep(100);
System.out.println(thread.getState()); // TERMINATED
При вызове run() поток остаётся в состоянии NEW или TERMINATED, новый поток не создаётся.
Множественный вызов start()
Чень важно: start() можно вызвать только один раз.
Thread thread = new Thread(() -> System.out.println("Выполнено"));
thread.start(); // ✅ OK
thread.start(); // ❌ IllegalThreadStateException
При этом run() можно вызывать сколько угодно раз, но это выполнит код в текущем потоке:
Thread thread = new Thread(() -> System.out.println("Выполнено"));
thread.run(); // ✅ Выполняет в main потоке
thread.run(); // ✅ Выполняет в main потоке (снова)
Практический пример ошибки
// НЕПРАВИЛЬНО
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println("Работаю " + Thread.currentThread().getName());
}).run(); // ← ошибка: код выполнится в main потоке
}
// ПРАВИЛЬНО
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println("Работаю " + Thread.currentThread().getName());
}).start(); // ← создаст 5 новых потоков
}
ExecutorService как альтернатива
В современной Java используют ExecutorService вместо прямого управления потоками:
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
System.out.println("Работаю " + Thread.currentThread().getName());
});
}
executor.shutdown();
Таблица сравнения
| Аспект | start() | run() |
|---|---|---|
| Создаёт новый поток | ✅ Да | ❌ Нет |
| Выполняет в текущем потоке | ❌ Нет | ✅ Да |
| Можно вызвать много раз | ❌ Один раз | ✅ Много раз |
| Параллельное выполнение | ✅ Да | ❌ Нет |
| Использование | Практически всегда | Редко, специальные случаи |
Когда нужна run()?
Очень редко, но есть исключения:
// Если нужно выполнить задачу синхронно в текущем потоке
Runnable task = () -> System.out.println("Выполнено");
task.run(); // Выполнить сразу
// Если вручную реализуешь свой Thread pool
class CustomThreadPool {
public void execute(Runnable task) {
// task.run(); // синхронное выполнение
}
}
Заключение
Правило 95%: Используй start(), а не run(). Метод run() — это то, что выполняется внутри, а start() — это то, что нужно вызывать для создания нового потока.
Ошибка вызова run() вместо start() — одна из самых трудноуловимых ошибок, потому что код кажется работающим, но выполняется в неправильном потоке и может привести к проблемам с производительностью и корректностью программы.