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

Каждый поток в Java на протяжении своей жизни проходит через серию состояний, описанных перечислением Thread.State. Понимание этих переходов помогает отлаживать многопоточные программы: видеть, почему поток «висит», когда он готов выполняться, а когда уже завершён.

Состояний шесть: NEW (создан, но не запущен), RUNNABLE (запущен и готов выполняться), BLOCKED (ждёт мьютекс), WAITING (ждёт без таймаута, например wait() или join()), TIMED_WAITING (ждёт с таймаутом, например sleep(ms)) и TERMINATED (поток завершился).

Схема состояний

    new Thread(...)
         |
         v
      [ NEW ]
         |
     start()
         v
    [ RUNNABLE ] <----+----+
     |   |   |        |    |
     |   |   v        |    |
     |   | [ BLOCKED ] синхр.блок освобождён
     |   v                  |
     | [ WAITING ] notify()/join завершён
     v
[ TIMED_WAITING ] sleep истёк
         |
     run() завершён
         v
   [ TERMINATED ]

Пример 1. NEW и RUNNABLE

public class StateNewRunnable {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 1_000_000; i++) { /* busy */ }
        });

        System.out.println("до start: " + t.getState());
        t.start();
        System.out.println("после start: " + t.getState());
    }
}

Output:

до start: NEW
после start: RUNNABLE

Пример 2. TIMED_WAITING при sleep

public class StateSleep {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try { Thread.sleep(500); } catch (InterruptedException ignored) {}
        });
        t.start();
        Thread.sleep(100);
        System.out.println("во время sleep: " + t.getState());
        t.join();
        System.out.println("после завершения: " + t.getState());
    }
}

Output:

во время sleep: TIMED_WAITING
после завершения: TERMINATED

Пример 3. BLOCKED на мониторе

public class StateBlocked {
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread holder = new Thread(() -> {
            synchronized (LOCK) {
                try { Thread.sleep(500); } catch (InterruptedException ignored) {}
            }
        }, "holder");

        Thread waiter = new Thread(() -> {
            synchronized (LOCK) {
                System.out.println("waiter вошёл в блок");
            }
        }, "waiter");

        holder.start();
        Thread.sleep(50);
        waiter.start();
        Thread.sleep(50);
        System.out.println("waiter: " + waiter.getState());
        holder.join();
        waiter.join();
    }
}

Output:

waiter: BLOCKED
waiter вошёл в блок

Пример 4. WAITING при join

public class StateJoin {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try { Thread.sleep(400); } catch (InterruptedException ignored) {}
        }, "worker");

        Thread observer = new Thread(() -> {
            try { worker.join(); } catch (InterruptedException ignored) {}
        }, "observer");

        worker.start();
        observer.start();
        Thread.sleep(100);
        System.out.println("observer: " + observer.getState());
        observer.join();
        System.out.println("observer после: " + observer.getState());
    }
}

Output:

observer: WAITING
observer после: TERMINATED

Пример 5. Прерывание спящего потока

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(5000);
                System.out.println("проснулся сам");
            } catch (InterruptedException e) {
                System.out.println("прерван!");
            }
        });
        t.start();
        Thread.sleep(200);
        t.interrupt();
        t.join();
        System.out.println("state: " + t.getState());
    }
}

Output:

прерван!
state: TERMINATED

Подводные камни

Предупреждение

Состояние RUNNABLE не означает «прямо сейчас выполняется на CPU». Это значит «поток может выполняться, как только планировщик его выберет».

Предупреждение

Thread.getState() подходит для отладки, но не для управления логикой — состояние может измениться между чтением и проверкой.

Совет

В AlashEd-проектах с фоновым опросом датчиков полезно вызвать thread.interrupt() для корректного завершения, а внутри цикла проверять Thread.currentThread().isInterrupted().

См. также

Примечание

Материал подготовлен на основе раздела «Concurrency» из Oracle Java Tutorials (docs.oracle.com/javase/tutorial/essential/concurrency/) и распространяется в соответствии с Oracle Free Documentation License. Тексты и примеры кода написаны заново на русском языке для AlashEd Wiki.