Чем различаются методы start() и run() у потока?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между методами 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()→ БЕГИ по уже есть дорожке (в текущем потоке)