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

Чем различаются методы start() и run() у потока?

1.0 Junior🔥 171 комментариев
#Soft Skills и карьера

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

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

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

Разница между методами start() и run() у потока

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

start() — создаёт новый поток

Метод start() — это правильный способ запустить поток. Он:

  • Создаёт новый поток в операционной системе
  • Запускает его асинхронно в фоне
  • Вызывает метод run() внутри этого нового потока
  • Возвращает управление сразу же
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("Выполняется в потоке: " + Thread.currentThread().getName());
            for (int i = 0; i < 5; i++) {
                System.out.println("Итерация " + i);
                Thread.sleep(500);
            }
        });

        System.out.println("До start()");
        thread.start(); // ✓ Правильно - создаёт новый поток
        System.out.println("После start() - выполняется сразу");

        // Основной поток не ждёт и продолжает работу
        Thread.sleep(3000);
    }
}

// Вывод:
// До start()
// После start() - выполняется сразу
// Выполняется в потоке: Thread-0
// Итерация 0
// Итерация 1
// Итерация 2
// Итерация 3
// Итерация 4

Ключевая особенность: основной поток и созданный поток выполняются параллельно.

run() — выполняет в текущем потоке

Метод run() — это неправильный способ запустить поток. Он:

  • НЕ создаёт новый поток
  • Выполняется в текущем потоке (обычно в главном потоке)
  • Блокирует выполнение до завершения
  • Нет параллелизма
public class BadThreadExample {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("Выполняется в потоке: " + Thread.currentThread().getName());
            for (int i = 0; i < 5; i++) {
                System.out.println("Итерация " + i);
                Thread.sleep(500);
            }
        });

        System.out.println("До run()");
        thread.run(); // ✗ НЕПРАВИЛЬНО - выполняет в текущем потоке!
        System.out.println("После run()");
    }
}

// Вывод:
// До run()
// Выполняется в потоке: main
// Итерация 0
// Итерация 1
// Итерация 2
// Итерация 3
// Итерация 4
// После run() - выполнится только после завершения потока!

Проблема: весь код выполняется последовательно в главном потоке. Нет параллелизма!

Сравнение: start() vs run()

Характеристикаstart()run()
Создаёт новый поток✓ Да✗ Нет
Асинхронное выполнение✓ Да✗ Нет
Параллелизм✓ Да✗ Нет
Возвращает управление сразу✓ Да✗ Нет (блокирует)
Поток, где выполняется кодНовый потокТекущий поток
Можно вызвать несколько раз✗ Нет (IllegalThreadStateException)✓ Да (но это плохая практика)

Визуальное объяснение

start() - ПРАВИЛЬНО:

Время →
Главный поток:  ┌─ start() ─┐ [продолжает работу]
Поток 1:                     └─ выполняет run() ─┘

Оба потока выполняются ОДНОВРЕМЕННО!

---

run() - НЕПРАВИЛЬНО:

Время →
Главный поток:  ┌──── run() выполняется ────┐ [продолжает]

Усё в одном потоке, ждём завершения!

Практический пример: Правильная работа с потоками

public class ProperThreading {
    public static void main(String[] args) throws InterruptedException {
        // Задача 1: Загрузка данных
        Thread downloadThread = new Thread(() -> {
            System.out.println("Начало загрузки данных...");
            try {
                Thread.sleep(3000);
                System.out.println("Загрузка завершена!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "Download-Thread");

        // Задача 2: Обработка UI
        Thread uiThread = new Thread(() -> {
            for (int i = 0; i < 4; i++) {
                System.out.println("UI остаётся отзывчивым (итерация " + i + ")");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "UI-Thread");

        // ✓ Правильно: оба потока начинают выполняться одновременно
        downloadThread.start();
        uiThread.start();

        System.out.println("Главный поток продолжает работу");

        // Ждём завершения обоих потоков
        downloadThread.join(); // Ждём загрузки
        uiThread.join();       // Ждём UI
        System.out.println("Все потоки завершены");
    }
}

// Вывод (асинхронный, порядок может варьироваться):
// Главный поток продолжает работу
// Начало загрузки данных...
// UI остаётся отзывчивым (итерация 0)
// UI остаётся отзывчивым (итерация 1)
// UI остаётся отзывчивым (итерация 2)
// Загрузка завершена!
// UI остаётся отзывчивым (итерация 3)
// Все потоки завершены

Когда можно вызвать start() только один раз

public class ThreadLifecycle {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Выполняется");
        });

        thread.start(); // ✓ OK - первый вызов
        // thread.start(); // ✗ IllegalThreadStateException!
        // Нельзя перезапустить поток

        // Решение: создай новый поток
        Thread thread2 = new Thread(() -> {
            System.out.println("Выполняется снова");
        });
        thread2.start(); // ✓ OK
    }
}

Правильное использование с наследованием Thread

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Выполняется в отдельном потоке");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // ✓ Правильно - создаёт новый поток
        // thread.run(); // ✗ Неправильно - выполнит в главном потоке
    }
}

Правильное использование с Runnable

public class RunnableExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("Выполняется в отдельном потоке");
        };

        Thread thread = new Thread(task);
        thread.start(); // ✓ Правильно
        // task.run();   // ✗ Неправильно - выполнит в главном потоке
    }
}

Заключение: Golden Rule

ВСЕГДА вызывай start(), а не run()!

Чтобы запустить поток:

  • thread.start() — создаёт новый поток, асинхронное выполнение
  • thread.run() — выполняет в текущем потоке, блокирует

Метод run() нужен только для переопределения в классе Thread, а не для вызова напрямую. Если вызвал run() напрямую — это, скорее всего, ошибка.

Визуальный паттерн для запоминания:

  • start() → СТАРТ забега (создай новую дорожку)
  • run() → БЕГИ по уже есть дорожке (в текущем потоке)
Чем различаются методы start() и run() у потока? | PrepBro