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

В чем разница между запуском интерфейса Runnable run и start?

1.6 Junior🔥 111 комментариев
#Основы Java

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

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

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

Разница между 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() — одна из самых трудноуловимых ошибок, потому что код кажется работающим, но выполняется в неправильном потоке и может привести к проблемам с производительностью и корректностью программы.