Найти тему
Nuances of programming

Состояния потоков в Java

Оглавление

Источник: Nuances of Programming

«Гораздо проще уже спроектировать класс потокобезопасным, чем модернизировать его позже».
― Брайан Гетц.

Состояния потоков (codeGym)
Состояния потоков (codeGym)

Потоки Java играют важную роль в параллельном программировании. Поток в любой момент времени находится только в одном из показанных на схеме ниже состояний:

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

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

1. New

Когда создается новый поток, он находится в состоянии New , причем запускаться поток еще не начал. Не начал запускаться его код, ему еще предстоит выполниться.

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

Thread newThread = new newThreadClass();

Объект Thread пуст, и ресурсы для потока недоступны. Если пользователь вызовет какой-то другой метод, кроме start() , произойдет ошибка IllegalThreadStateExecption .

2. Runnable

Поток, готовый к запуску, переходит в состояние runnable .

Thread newThread = new newThreadClass();
newThread.start();

В этом состоянии поток либо запускается, либо готов в любой момент запуститься. Дальше свою работу выполняет планировщик потоков, предоставляющий время для выполнения потока. В большинстве операционных систем он каждому потоку выделяет небольшое количество процессорного времени. Когда это происходит, все такие потоки, которые готовы к запуску, ждут центральный процессор, а выполняемый в этот момент времени поток находится в состоянии runnable .

3. Blocked

При попытке выполнить задачу, которая не может быть завершена в данный момент времени, поток из состояния runnable переходит в состояние blocked . И ждет, пока задача не будет завершена.

Например, когда поток ожидает завершения операций ввода-вывода, он находится в состоянии blocked . Поток в этом состоянии не может дальше продолжать выполнение до тех пор, пока не перейдет в состояние runnable . Планировщик потоков повторно активирует blocked/waiting (блокированный/ожидающий) поток и планирует его выполнение. Любой поток, находясь в одном из этих состояний, не потребляет процессорное время.

4. Waiting

Ожидающий поток (FastThread)
Ожидающий поток (FastThread)

Когда поток находится в состоянии waiting , он ждет другой поток, связанный условием. Когда это условие выполняется, планировщик получает уведомление и вызываются методы notify () или notifyAll() . В этом случае ожидающий поток переходит в состояние runnable .

Если выполняемый в это время поток переходит в состояние blocked/waiting , планировщик дает добро на выполнение ожидающего потока, перешедшего в состояние runnable . Именно планировщик потоков определяет, какой поток должен выполняться.

5. Time waiting

Поток находится в состоянии runnable . Теперь он вызывает метод sleep(t) , wait(t) или join(t) с неким промежутком времени в качестве параметра и переходит в состояние time waiting . Поток остается в этом состоянии до тех пор, пока время ожидания не выйдет или пока не будет получено уведомление. Например, когда поток вызывает sleep или условное ожидание, он переходит в состояние timed waiting (ожидание с ограничением по времени). Как только время выйдет, поток вернется в состояние runnable .

6. Terminate

Поток завершается по любой из следующих причин:

· Поток завершается в обычном режиме, когда код потока полностью выполнен программой.

· При выполнении потока произошло какое-то нештатное событие, сопровождаемое появлением ошибки, например ошибки сегментации или необработанного исключения.

Планирование потоков

Говоря о потоках и состояниях потоков в Java, не стоит забывать о планировании потоков.

Планирование потоков применяется для определения приоритета потоков. При запуске потока ему достается определенный приоритет: как максимум MAX_PRIORITY= 10 и как минимум MIN_PRIORITY = 1 .

Обычный приоритет будет равен пяти (NORMAL_PRIORITY = 5 ).

Согласно правилу планирования потоков, добро на выполнение всегда дается потоку с более высоким приоритетом, а поток с низким уровнем приоритета переходит в состояние waiting .

Если потоки имеют равный приоритет, то они будут выполняться согласно методу Round-Robin, т. е. перебором по круговому циклу.

В этом сценарии racer[0] и racer[1] имеют значение приоритета 7 , а у racer[3] оно равно 3 .

То есть racer[0] и racer[1] будут выполняться как первый поток, так как у них самый высокий приоритет среди потоков в состоянии RUNNABLE .

Что касается метода Round-Robin: если racer[0] будет выбран первым на выполнение, то другие будут ожидать его завершения. После чего начнет выполняться racer[1] . Они будут выполняться до тех пор, пока их процесс не будет завершен. И только затем запустится поток racer[3] , имеющий самый низкий приоритет. Он будет выполняться без каких-либо помех до завершения своего процесса.

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

Читайте также:

Читайте нас в Telegram , VK

Перевод статьи Ravidu Perera : States of Thread in Java