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

В Java поток проходит четыре состояния в течение своего жизненного цикла:

- Новый
- Работает
- Ожидание — [Заблокировано, Ожидание, Ожидание по времени]
- Не работает

Новый — когда поток создан и готов к использованию. Это состояние, когда мы еще не начали поток.
Выполняется — поток переходит в это состояние после того, как мы его запустили. Поток выполняет свою задачу в этом состоянии
Ожидание — состояние ожидания соответствует трем сценариям — Заблокировано, Ожидание, Ожидание по времени, поскольку поток может войти в это состояние в различных сценариях. Например, когда поток пытается получить блокировку, он входит в состояние Blocking; когда поток ожидает выполнения другого потока, тогда поток переходит в состояние ожидания, а когда поток ожидает только указанное количество времени для выполнения другого потока, поток входит в состояние TimedWaiting. Поток возвращается в рабочее состояние, как только другие потоки выполнили или уведомили текущий поток о продолжении. Поток может бесконечно менять свое состояние из состояния выполнения в состояние ожидания и наоборот.
Dead — в это состояние поток переходит после полного выполнения, т.е. завершения своей задачи, или из-за каких-то исключений. Поток после выполнения не может быть запущен снова. Если мы попытаемся запустить поток в мертвом состоянии, мы получим IllegalStateException.
О блокировках мы поговорим в следующих постах.

Присоединение к потоку ()

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

Рассмотрим следующие примеры:

В этом примере мы создаем новый поток — threadTwo, который спит в течение двух секунд и считает до 1000. Затем он выводит сообщение о завершении своего выполнения. Если мы запустим эту программу, мы получим следующий вывод.

Как только threadTwo начинает выполняться, метод main() продолжает свое выполнение, он печатает — “ Выполнение основного метода» и завершает его выполнение. Параллельно выполняется threadTwo, считает до 1000, затем выводит — «Счетчик потока завершил выполнение, counter = 1000» и заканчивает выполнение.

Но что, если мы хотим, чтобы основной потокожидал выполнения threadTwo? Как мы можем этого добиться? Довольно просто, использование join() решает эту проблему.

Отрезанный код почти такой же, как и отрезанный выше, с небольшой разницей. В строке 17, сразу после threadTwo.start(), мы добавляем метод, вызывающий threadTwo.join(). Если мы запустим эту программу, мы получим следующий вывод.

Порядок операторов печати в этом примере изменился. Сразу после запуска threadTwo mainThread вызывает join()метод. Это приводит к тому, что main thread прекращает выполнение и ожидает threadTwo для завершения его выполнения. Как видно из вывода, threadTwo выполняется, считает до 1000, выводит сообщение —«Счетчик потока завершил выполнение, counter = 1000» и завершает его выполнение. После этого mainThread продолжает свое выполнение и выводит следующий оператор — «Выполняется основной метод».

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

Нити демона

В Java существует два типа потоков — пользовательские(те, которые мы создаем) потоки и потоки демона. Когда Java-программа запускается, сразу же начинает выполнение один потокглавный поток. Основной поток запускает основной метод. Мы можем создавать новые потоки из основного потока. Основной поток завершает выполнение последним потоком, поскольку он выполняет различные операции завершения работы.

Потоки демона функционируют как вспомогательные потоки, они выполняют и выполняют операции в фоновом режиме. Например, сборка мусора в Java выполняется как поток демона.

В Основном потоке мы можем создать столько потоков, сколько необходимо (дочерние потоки основного потока). Более того, класс Thread предоставляет метод setDaemon(boolean), который позволяет пользовательскому потоку превратиться в поток демона.

Потоки демона:
— поток с низким приоритетом, работающий в фоновом режиме
— поток демона завершается JVM, когда все остальные рабочие потоки завершают выполнение.
— обычно потоки демона используются для операций ввода-вывода и служб (в смартфонах для связи Bluetooth или NFC)

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

Фрагмент кода выше показывает, что печатает имя потока, который запускает метод main(). Thread.currentThread() — возвращает текущий запущенный поток.

Давайте определим наш собственный поток демона. Мы создадим поток демона, который будет печатать сообщение каждую секунду.

В приведенном выше фрагменте кода мы создаем два потока и называем их worker и daemon. Поток — worker приостанавливается на три секунды, печатает сообщение —«Поток завершает выполнение с именем: Worker», а затем завершает выполнение .
Поток — демон каждую секунду выводит сообщение — "Поток выполняется с именем: Демон" и никогда не выходит из цикла. Мы устанавливаем daemonFlag как true для потока — демона в строке 25. Он будет работать бесконечно, пока другие потоки не оживут.
В строках 26, 27 мы начинаем наши потоки — worker и daemon. Основной поток продолжает выполнение — выводит сообщение "Поток выполняется с именем: main" и завершает выполнение.
Как только рабочий поток завершит свое выполнение, рабочих потоков не останется, а поток — демон будет завершен JVM.
Если мы запустим программу программу, мы получим следующий вывод.

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

В этой серии статей о многопоточности мы обсудили жизненный цикл потока, метод Thread.join() и потоки демона. Самое интересное еще впереди. Подписывайтесь и следите за обновлениями, чтобы первыми читать новые статьи.

Если вы пропустили предыдущие статьи из серии «Многопоточность», рекомендуем прочитать и их.