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

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

 

1. Вступление

В этой статье мы подробно обсудим основную концепцию Java - жизненный цикл потока.

Мы будем использовать краткую иллюстрированную диаграмму и, конечно, практические фрагменты кода, чтобы лучше понять эти состояния во время выполнения потока.

Чтобы начать разбираться в потоках в Java, лучше всего начать сthis article при создании потока.

2. Многопоточность в Java

In the Java language, multithreading is driven by the core concept of a Thread. В течение своего жизненного цикла потоки проходят через различные состояния:

image

3. Жизненный цикл потока в Java

Классjava.lang.Thread содержитstatic State enum –, который определяет его потенциальные состояния. В любой момент времени поток может находиться только в одном из следующих состояний:

  1. NEW – недавно созданный поток, который еще не начал выполнение

  2. RUNNABLE – либо запущен, либо готов к выполнению, но ожидает выделения ресурсов

  3. BLOCKED – ожидает получения блокировки монитора для входа или повторного входа в синхронизированный блок / метод

  4. WAITING – ожидает, пока какой-либо другой поток выполнит определенное действие без ограничения по времени

  5. TIMED_WAITING – ожидает, пока какой-либо другой поток выполнит определенное действие в течение указанного периода

  6. TERMINATED – завершил свое выполнение

Все эти состояния показаны на диаграмме выше; Давайте теперь подробно обсудим каждый из них.

3.1. New

A NEW Thread (or a Born Thread) is a thread that’s been created but not yet started. Он остается в этом состоянии, пока мы не запустим его с помощью методаstart().

В следующем фрагменте кода показан вновь созданный поток, который находится в состоянииNEW:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
Log.info(t.getState());

Поскольку мы не запустили упомянутый поток, методt.getState() печатает:

NEW

3.2. Runnable

Когда мы создали новый поток и вызвали для него методstart(), он перешел из состоянияNEW в состояниеRUNNABLE. Threads in this state are either running or ready to run, but they’re waiting for resource allocation from the system.

В многопоточной среде планировщик потоков (который является частью JVM) выделяет фиксированное количество времени для каждого потока. Таким образом, он выполняется в течение определенного времени, а затем передает управление другим потокамRUNNABLE.

Например, давайте добавим методt.start() к нашему предыдущему коду и попытаемся получить доступ к его текущему состоянию:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
Log.info(t.getState());

Этот кодmost likely возвращает результат как:

RUNNABLE

Обратите внимание, что в этом примере не всегда гарантируется, что к тому времени, когда наш элемент управления достигнетt.getState(), он все еще будет в состоянииRUNNABLE.

Может случиться так, что он был немедленно запланированThread-Scheduler и может завершить выполнение. В таких случаях мы можем получить другой результат.

3.3. блокированный

Поток находится в состоянииBLOCKED, когда он в настоящее время не может быть запущен. It enters this state when it is waiting for a monitor lock and is trying to access a section of code that is locked by some other thread.

Попробуем воспроизвести это состояние:

public class BlockedState {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new DemoThreadB());
        Thread t2 = new Thread(new DemoThreadB());

        t1.start();
        t2.start();

        Thread.sleep(1000);

        Log.info(t2.getState());
        System.exit(0);
    }
}

class DemoThreadB implements Runnable {
    @Override
    public void run() {
        commonResource();
    }

    public static synchronized void commonResource() {
        while(true) {
            // Infinite loop to mimic heavy processing
            // 't1' won't leave this method
            // when 't2' try to enters this
        }
    }
}

В этом коде:

  1. Мы создали два разных потока -t1 иt2.

  2. t1 запускается и входит в синхронизированный методcommonResource(); это означает, что к нему может получить доступ только один поток; все остальные последующие потоки, которые пытаются получить доступ к этому методу, будут заблокированы от дальнейшего выполнения, пока текущий не завершит обработку

  3. Когдаt1 входит в этот метод, он остается в бесконечном цикле while; это просто имитирует тяжелую обработку, чтобы все другие потоки не могли войти в этот метод

  4. Теперь, когда мы запускаемt2, он пытается ввести методcommonResource(), к которому уже обращаетсяt1,, поэтомуt2 будет сохранен в состоянииBLOCKED

Находясь в этом состоянии, мы вызываемt2.getState() и получаем результат как:

BLOCKED

3.4. Ожидание

A thread is in WAITING state when it’s waiting for some other thread to perform a particular action.According to JavaDocs, любой поток может войти в это состояние, вызвав любой из следующих трех методов:

  1. object.wait()

  2. thread.join() или

  3. LockSupport.park()

Обратите внимание, что вwait() иjoin() - мы не определяем период тайм-аута, так как этот сценарий рассматривается в следующем разделе.

У нас естьa separate tutorial, в котором подробно обсуждается использованиеwait(),notify() иnotifyAll().

А пока попробуем воспроизвести это состояние:

public class WaitingState implements Runnable {
    public static Thread t1;

    public static void main(String[] args) {
        t1 = new Thread(new WaitingState());
        t1.start();
    }

    public void run() {
        Thread t2 = new Thread(new DemoThreadWS());
        t2.start();

        try {
            t2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }
    }
}

class DemoThreadWS implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }

        Log.info(WaitingState.t1.getState());
    }
}

Давайте обсудим, что мы здесь делаем:

  1. Мы создали и запустилиt1

  2. t1 создаетt2 и запускает его

  3. Пока обработкаt2 продолжается, мы вызываемt2.join(), это переводитt1 в состояниеWAITING, покаt2 не завершит выполнение

  4. Посколькуt1 ожидает завершенияt2, мы вызываемt1.getState() изt2

Результат, как и следовало ожидать:

WAITING

3.5. Время ожидания

Поток находится в состоянииTIMED_WAITING, когда он ожидает, пока другой поток выполнит определенное действие в течение установленного периода времени.

According to JavaDocs, есть пять способов перевести поток в состояниеTIMED_WAITING:

  1. thread.sleep(long millis)

  2. wait(int timeout) илиwait(int timeout, int nanos)

  3. thread.join(long миллис)

  4. LockSupport.parkNanos

  5. LockSupport.parkUntil

Чтобы узнать больше о различиях междуwait() иsleep() в Java, взгляните наthis dedicated article here.

А пока давайте попробуем быстро воспроизвести это состояние:

public class TimedWaitingState {
    public static void main(String[] args) throws InterruptedException {
        DemoThread obj1 = new DemoThread();
        Thread t1 = new Thread(obj1);
        t1.start();

        // The following sleep will give enough time for ThreadScheduler
        // to start processing of thread t1
        Thread.sleep(1000);
        Log.info(t1.getState());
    }
}

class DemoThread implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }
    }
}

Здесь мы создали и запустили потокt1, который переходит в состояние сна с периодом ожидания 5 секунд; вывод будет:

TIMED_WAITING

3.6. Отменено

Это состояние мертвой нити. It’s in the TERMINATED state when it has either finished execution or was terminated abnormally.с

У нас естьa dedicated article, в котором обсуждаются различные способы остановки потока.

Попробуем достичь этого состояния на следующем примере:

public class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();
        // The following sleep method will give enough time for
        // thread t1 to complete
        Thread.sleep(1000);
        Log.info(t1.getState());
    }

    @Override
    public void run() {
        // No processing in this block
    }
}

Здесь, когда мы запустили потокt1, уже следующий операторThread.sleep(1000) дает достаточно времени для завершенияt1, поэтому эта программа дает нам результат в виде:

TERMINATED

4. Заключение

В этом уроке мы узнали о жизненном цикле потока в Java. Мы рассмотрели все шесть состояний, определенных перечислениемThread.State, и воспроизвели их на быстрых примерах.

Хотя фрагменты кода будут давать одинаковый вывод почти на каждой машине, в некоторых исключительных случаях мы можем получить несколько разных выводов, поскольку точное поведение планировщика потоков не может быть определено.

И, как всегда, здесь используются фрагменты кодаavailable on GitHub.