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

Зачем нужен метод run() в классе Thread?

1.0 Junior🔥 251 комментариев
#Многопоточность

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

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

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

Зачем нужен метод run() в классе Thread?

Метод run() — это контрольная точка, которая определяет, какой код должен выполняться в отдельном потоке. Это основной механизм для определения поведения потока в Java.

Базовое понимание

Thread vs run():

  • Класс Thread — это конструкция Java для создания новых потоков
  • Метод run() — это определение того, ЧТО будет делать этот поток
// Создание класса, расширяющего Thread
public class MyThread extends Thread {
    @Override
    public void run() {
        // Этот код выполняется в отдельном потоке
        System.out.println("Выполняется в потоке: " + Thread.currentThread().getName());
    }
}

// Использование
public static void main(String[] args) {
    MyThread thread = new MyThread();
    thread.start();  // Запускает run() в новом потоке
    // Главный поток продолжает выполняться параллельно
}

Критичное различие: start() vs run()

❌ НЕПРАВИЛЬНО: вызвать run() напрямую

MyThread thread = new MyThread();
thread.run();  // ОШИБКА!
// Это просто вызывает метод в ТЕКУЩЕМ потоке
// Никакого нового потока не создается
// Выполняется синхронно, как обычный метод

✅ ПРАВИЛЬНО: вызвать start()

MyThread thread = new MyThread();
thread.start();  // Создает новый поток и вызывает run() в нем
// Главный поток продолжает выполняться параллельно

Как это работает изнутри

Когда вы вызываете start():

  1. JVM создает новый поток операционной системы
  2. JVM регистрирует run() как точку входа для этого потока
  3. Когда ОС начинает выполнять этот поток, JVM вызывает run()
  4. Главный поток и новый поток работают параллельно
public static void main(String[] args) throws InterruptedException {
    MyThread thread = new MyThread();
    thread.start();  // Создает новый поток
    System.out.println("Главный поток продолжает");  // Выполняется сразу
    thread.join();  // Ждет, пока thread закончит
    System.out.println("Все потоки завершены");
}

// Output:
// Главный поток продолжает
// Выполняется в потоке: Thread-0
// Все потоки завершены

Почему нельзя вызвать run() напрямую?

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        
        // ❌ Вызов run() напрямую
        long start = System.currentTimeMillis();
        thread.run();  // Выполняется в главном потоке
        System.out.println("run() заняла: " + 
            (System.currentTimeMillis() - start) + " ms");
        System.out.println("Главный поток всё еще заблокирован!");
        
        // ✅ Правильное использование start()
        start = System.currentTimeMillis();
        thread.start();  // Создает новый поток
        System.out.println("start() заняла: " + 
            (System.currentTimeMillis() - start) + " ms");
        System.out.println("Главный поток продолжает параллельно!");
        thread.join();
    }
}

Реализация Runnable интерфейса

Зачасто используется Runnable вместо наследования Thread:

// Способ 1: Наследование Thread (используется редко)
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Поток выполняется");
    }
}

MyThread thread = new MyThread();
thread.start();

// Способ 2: Реализация Runnable (предпочтительно)
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Поток выполняется");
    }
}

Thread thread = new Thread(new MyRunnable());
thread.start();

// Способ 3: Lambda (Java 8+)
Thread thread = new Thread(() -> {
    System.out.println("Поток выполняется");
});
thread.start();

Жизненный цикл потока и run()

NEW --> RUNNABLE --> RUNNING --> TERMINATED
         ↑                ↓
         +-- WAITING -----+
         +-- BLOCKED -----+

Вот как run() вписывается в этот цикл:

MyThread thread = new MyThread();
// Состояние: NEW

thread.start();
// Состояние: RUNNABLE (планировщик выберет это время)
// Когда планировщик переведет в RUNNING:
// --> JVM автоматически вызывает run()
// Выполняется код в run()
// Когда run() завершается --> TERMINATED

Обработка исключений в run()

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            // код
            int result = 10 / 0;  // ArithmeticException
        } catch (Exception e) {
            // Очень важно ловить исключения!
            // Если не поймаешь, поток умрет молча
            System.err.println("Ошибка в потоке: " + e);
        }
    }
}

// Альтернатива: UncaughtExceptionHandler
thread.setUncaughtExceptionHandler((thread1, exception) -> {
    System.err.println("Необработанное исключение: " + exception);
});

Практический пример: Загрузка данных в фоне

public class DataLoader extends Thread {
    private List<String> data;
    
    @Override
    public void run() {
        System.out.println("Начинаем загрузку в потоке: " + 
            Thread.currentThread().getName());
        try {
            data = downloadData();  // Дорогая операция
            System.out.println("Загрузка завершена");
        } catch (Exception e) {
            System.err.println("Ошибка загрузки: " + e);
        }
    }
    
    public List<String> downloadData() throws Exception {
        Thread.sleep(3000);  // Имитация загрузки
        return Arrays.asList("data1", "data2");
    }
}

// Использование
public static void main(String[] args) throws InterruptedException {
    DataLoader loader = new DataLoader();
    loader.start();  // Загрузка начинается в фоне
    System.out.println("Главный поток работает дальше");
    loader.join();  // Ждем завершения
    System.out.println("Главный поток: загрузка закончена");
}

Итоги

Метод run() нужен для:

  1. Определения кода — что должен выполнять поток
  2. Отделения логики — отделить поведение потока от его управления
  3. Контракта — всё, что расширяет Thread или реализует Runnable, ДОЛЖНО определить run()
  4. Точки входа — JVM знает, какой метод вызывать при запуске потока

Критично помнить: start() создает поток и вызывает run(), а run() вызванный напрямую — это просто синхронный вызов метода!

Зачем нужен метод run() в классе Thread? | PrepBro