thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Runnable и Thread

В Java многопоточность программы организуется с помощью интерфейса Runnable и класса Thread, который наследуется от Runnable. Первый способ более гибкий, второй – проще.

Та часть кода, которая должна выполняться в отдельном потоке, выносится в свой класс, имеющий переопределенный метод run(). Код метода run() выполняется, когда к объекту типа Thread применяется метод start(). Непосредственный вызов run() новый поток не создает.

Здесь обработка исключений необходима из-за статического метода sleep(), который приостанавливает выполнение текущего потока. Данный метод часто используют в дочерних потоках, когда они должны выполнять какое-либо действие постоянно, но не бесперебойно. Например, периодически проверять доступность ресурса.

Метод join() заставляет текущий поток ждать завершения нити, к которой применяется. Только после этого текущий поток может продолжить выполнение своего кода.

В данном случае мы создаем класс-наследник от Runnable. Объект типа Runnable или его производное передается в конструктор объекта типа Thread. После этого поток запускается.

Другой вариант – когда пользовательский класс является наследником Thread:

Этот вариант не подходит, если класс для организации отдельного потока должен наследоваться от другого класса (не Thread). Поскольку в Java нет множественного наследования классов, приходится использовать наследование от интерфейса Runnable. Также данный подход не дает возможности запускать несколько потоков на основе одного объекта. Так в первом примере мы могли бы передать единственный объект anotherRun в несколько объектов типа Thread.

Напомним, библиотечный класс Thread сам является наследником Runnable.

Если в отдельный поток обособляется небольшая подзадача, можно использовать неименованный класс:

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

Для прерывания выполнения нити, если это необходимо, используется метод interrupt(), который устанавливает переменную isInterrupt в значение true. К коде пользовательского класса, унаследованного от Runnable/Thread, это переменная должна проверяться. Отсюда следует, что на самом деле в Java нет возможности прервать поток извне, поток может остановиться только сам.

С другой стороны, в метод sleep() уже встроена проверка переменной isInterrupt, поэтому проверку вручную опускают. Если sleep() считывает наличие прерывания, то генерирует исключение.

В примере основной поток ожидает ввод данных, в это время выполняется вторая нить. Но как только вы нажмете Enter, выполнится метод interrupt(). В свою очередь метод sleep() прочитает значение переменной isInterrupt класса Thread и сгенерирует исключение InterruptedException.

Если sleep() не используется, то isInterrupt проверяется вручную методом isInterrupted(). Следующий пример содержит ошибку, приводящую к зацикливанию:

Мы могли бы ожидать, что через 2 секунды сработает метод interrupt(), который прервет дочернюю нить. Однако, поскольку в ней не проверяется значение isInterrupt, цикл продолжает работать. Корректный код может выглядеть так:

При наследовании от Runnable текущий поток через this получить нельзя. Его получают, вызывая соответствующий метод класса Thread:

Источник

Русские Блоги

Thread VS Runnable в многопоточности Java

Thread VS Runnable

Thread VS Runnable

Сравнение двух методов:

подводить итоги: Используйте Runnable таким образом для создания потоков.

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

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности
Жизненный цикл потока:

Готово: после создания объекта потока вызывается метод start () потока (Примечание: в это время поток просто входит в очередь потоков, ожидая службы ЦП, и имеет условия для выполнения, но не обязательно, что он начал работать)

Выполнить: как только поток в состоянии готовности получает ресурсы ЦП, он переходит в состояние выполнения и начинает выполнять логику метода run ().

Завершение: выполняется метод потока run () или поток вызывает метод stop () (этот метод исключен), поток переходит в состояние завершения.

Блокировка: в некоторых случаях выполняющийся поток по какой-либо причине временно отказывается от ресурсов ЦП, приостанавливает свое выполнение и переходит в состояние блокировки, например, при вызове метода sleep ().
Есть два типа потоков Java

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

Поток демона: работает в фоновом режиме, обслуживает другие потоки переднего плана

Как настроить поток демона:

Интеллектуальная рекомендация

AlamoFire Source Severtation Series (12) ось времени (временная шкала)

Эта статья приносит некоторые идеи о временной шкале в AlamoFire Преступность Перевод Timeline означает, что временная шкала может представлять событие с начала до конца узла времени. Концепция времен.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Анализ разницы между iOS initWithFrame, initWithCoder, awakeFromNib

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Исследование соответствия типовых версий рамы

Источник

Волшебный мир Java

воскресенье, 19 февраля 2012 г.

Многопоточность в Java. Часть 1

28 комментариев:

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

спасбо, прочитал перед сном, ещё раз вспомнил основы.

Не за что. Очень рад, что статья пригодилась.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

спасибо, не хватает только про wait и notify рассказать

Рад, что статья помогает.
Постараюсь в ближайшее время написать вторую часть статьи. Все никак руки не доходят. Но постараюсь исправиться 🙂

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Недавно начал изучать java. Многопоточность по Эккелю понимается гораздо труднее, а благодаря вашей статье разобрался. Большое спасибо! :))

Очень приятно это слышать. Значит нужно продолжать начатое дело. Постараюсь, более шире раскрыть данную тему.

Изучаю джава по Шилдту 8 издание, не мог норм понять многопотоки, спасибо вам, жду с нетерпением продолжения «синхронизации потоков и пакет concurrent.»

Надеюсь получилось донести мысль сказанного :).

Все понял, спасибо. Есть вопрос о deadlock. Там мне тоже сложно разобраться. В какое приблизительно время вам писать можно, чтоб вы по возможности отвечали, если есть желание помочь. Заранее спасибо.

Deadlock возникает когда поток или потоки переходят в режим ожидания/блокировки и выйти из них не могут. В итоге поток(и) никогда не завершат своей работы. Постараюсь на этих выходных написать статейку по этому поводу. А то все как-то давно собираюсь, да руки не доходят.

В какое время писать, даже не знаю. Иногда бываю днем, иногда вечером, как получается. Если смогу помочь, то с удовольствием помогу

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Спасибо, постараюсь писать дальше. Но со временем немного сложновато)

Этот комментарий был удален автором.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

доступно написано, спасибо большое автору!

Спасибо за статью, когда будет следующая обещеная Вами статья?)))

Спасибо, было полезно.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Отличная статья для начинающих) Спасибо!

Этот комментарий был удален автором.

Спасибо за статью! Не могу понять почему в последнем примере вывод начинается с результатов работы цикла фор, а не с фразы «Запускаем поток-демон: daemon». Ведь в мейне сперва запускается поток-демон, уже после начинается цикл фор. А в ране потока-демона цикл уайл начинается со строки «Запускаем поток-демон: daemon» и уже после её вывода запускается метод слип. Был бы признателен за пояснение.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Источник

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

Здравствуйте! В этой статье я вкратце расскажу вам о процессах, потоках, и об основах многопоточного программирования на языке Java. thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности
Наиболее очевидная область применения многопоточности – это программирование интерфейсов. Многопоточность незаменима тогда, когда необходимо, чтобы графический интерфейс продолжал отзываться на действия пользователя во время выполнения некоторой обработки информации. Например, поток, отвечающий за интерфейс, может ждать завершения другого потока, загружающего файл из интернета, и в это время выводить некоторую анимацию или обновлять прогресс-бар. Кроме того он может остановить поток загружающий файл, если была нажата кнопка «отмена».

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

Давайте начнем. Сначала о процессах.

Процессы

Процесс — это совокупность кода и данных, разделяющих общее виртуальное адресное пространство. Чаще всего одна программа состоит из одного процесса, но бывают и исключения (например, браузер Chrome создает отдельный процесс для каждой вкладки, что дает ему некоторые преимущества, вроде независимости вкладок друг от друга). Процессы изолированы друг от друга, поэтому прямой доступ к памяти чужого процесса невозможен (взаимодействие между процессами осуществляется с помощью специальных средств).

Для каждого процесса ОС создает так называемое «виртуальное адресное пространство», к которому процесс имеет прямой доступ. Это пространство принадлежит процессу, содержит только его данные и находится в полном его распоряжении. Операционная система же отвечает за то, как виртуальное пространство процесса проецируется на физическую память.

Схема этого взаимодействия представлена на картинке. Операционная система оперирует так называемыми страницами памяти, которые представляют собой просто область определенного фиксированного размера. Если процессу становится недостаточно памяти, система выделяет ему дополнительные страницы из физической памяти. Страницы виртуальной памяти могут проецироваться на физическую память в произвольном порядке.

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

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

Потоки

Один поток – это одна единица исполнения кода. Каждый поток последовательно выполняет инструкции процесса, которому он принадлежит, параллельно с другими потоками этого процесса.

Следует отдельно обговорить фразу «параллельно с другими потоками». Известно, что на одно ядро процессора, в каждый момент времени, приходится одна единица исполнения. То есть одноядерный процессор может обрабатывать команды только последовательно, по одной за раз (в упрощенном случае). Однако запуск нескольких параллельных потоков возможен и в системах с одноядерными процессорами. В этом случае система будет периодически переключаться между потоками, поочередно давая выполняться то одному, то другому потоку. Такая схема называется псевдо-параллелизмом. Система запоминает состояние (контекст) каждого потока, перед тем как переключиться на другой поток, и восстанавливает его по возвращению к выполнению потока. В контекст потока входят такие параметры, как стек, набор значений регистров процессора, адрес исполняемой команды и прочее…

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

Вот как это выглядит:

thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Смотреть картинку thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Картинка про thread и runnable что выбрать зачем нужно два вида реализации многопоточности. Фото thread и runnable что выбрать зачем нужно два вида реализации многопоточности

Цветные квадраты на рисунке – это инструкции процессора (зеленые – инструкции главного потока, синие – побочного). Выполнение идет слева направо. После запуска побочного потока его инструкции начинают выполняться вперемешку с инструкциями главного потока. Кол-во выполняемых инструкций за каждый подход не определено.

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

Запуск потоков

Каждый процесс имеет хотя бы один выполняющийся поток. Тот поток, с которого начинается выполнение программы, называется главным. В языке Java, после создания процесса, выполнение главного потока начинается с метода main(). Затем, по мере необходимости, в заданных программистом местах, и при выполнении заданных им же условий, запускаются другие, побочные потоки.

В языке Java поток представляется в виде объекта-потомка класса Thread. Этот класс инкапсулирует стандартные механизмы работы с потоком.

Запустить новый поток можно двумя способами:

Способ 1

Создать объект класса Thread, передав ему в конструкторе нечто, реализующее интерфейс Runnable. Этот интерфейс содержит метод run(), который будет выполняться в новом потоке. Поток закончит выполнение, когда завершится его метод run().

Для пущего укорочения кода можно передать в конструктор класса Thread объект безымянного внутреннего класса, реализующего интерфейс Runnable:

Способ 2

Создать потомка класса Thread и переопределить его метод run():

В приведённом выше примере в методе main() создается и запускается еще один поток. Важно отметить, что после вызова метода mSecondThread.start() главный поток продолжает своё выполнение, не дожидаясь пока порожденный им поток завершится. И те инструкции, которые идут после вызова метода start(), будут выполнены параллельно с инструкциями потока mSecondThread.

Для демонстрации параллельной работы потоков давайте рассмотрим программу, в которой два потока спорят на предмет философского вопроса «что было раньше, яйцо или курица?». Главный поток уверен, что первой была курица, о чем он и будет сообщать каждую секунду. Второй же поток раз в секунду будет опровергать своего оппонента. Всего спор продлится 5 секунд. Победит тот поток, который последним изречет свой ответ на этот, без сомнения, животрепещущий философский вопрос. В примере используются средства, о которых пока не было сказано (isAlive() sleep() и join()). К ним даны комментарии, а более подробно они будут разобраны дальше.

В приведенном примере два потока параллельно в течении 5 секунд выводят информацию на консоль. Точно предсказать, какой поток закончит высказываться последним, невозможно. Можно попытаться, и можно даже угадать, но есть большая вероятность того, что та же программа при следующем запуске будет иметь другого «победителя». Это происходит из-за так называемого «асинхронного выполнения кода». Асинхронность означает то, что нельзя утверждать, что какая-либо инструкция одного потока, выполнится раньше или позже инструкции другого. Или, другими словами, параллельные потоки независимы друг от друга, за исключением тех случаев, когда программист сам описывает зависимости между потоками с помощью предусмотренных для этого средств языка.

Теперь немного о завершении процессов…

Завершение процесса и демоны

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

Однако это правило не относится к особому виду потоков – демонам. Если завершился последний обычный поток процесса, и остались только потоки-демоны, то они будут принудительно завершены и выполнение процесса закончится. Чаще всего потоки-демоны используются для выполнения фоновых задач, обслуживающих процесс в течение его жизни.

Объявить поток демоном достаточно просто — нужно перед запуском потока вызвать его метод setDaemon(true) ;
Проверить, является ли поток демоном, можно вызвав его метод boolean isDaemon() ;

Завершение потоков

В Java существуют (существовали) средства для принудительного завершения потока. В частности метод Thread.stop() завершает поток незамедлительно после своего выполнения. Однако этот метод, а также Thread.suspend(), приостанавливающий поток, и Thread.resume(), продолжающий выполнение потока, были объявлены устаревшими и их использование отныне крайне нежелательно. Дело в том что поток может быть «убит» во время выполнения операции, обрыв которой на полуслове оставит некоторый объект в неправильном состоянии, что приведет к появлению трудноотлавливаемой и случайным образом возникающей ошибке.

Вместо принудительного завершения потока применяется схема, в которой каждый поток сам ответственен за своё завершение. Поток может остановиться либо тогда, когда он закончит выполнение метода run(), (main() — для главного потока) либо по сигналу из другого потока. Причем как реагировать на такой сигнал — дело, опять же, самого потока. Получив его, поток может выполнить некоторые операции и завершить выполнение, а может и вовсе его проигнорировать и продолжить выполняться. Описание реакции на сигнал завершения потока лежит на плечах программиста.

Java имеет встроенный механизм оповещения потока, который называется Interruption (прерывание, вмешательство), и скоро мы его рассмотрим, но сначала посмотрите на следующую программку:

Incremenator — поток, который каждую секунду прибавляет или вычитает единицу из значения статической переменной Program.mValue. Incremenator содержит два закрытых поля – mIsIncrement и mFinish. То, какое действие выполняется, определяется булевой переменной mIsIncrement — если оно равно true, то выполняется прибавление единицы, иначе — вычитание. А завершение потока происходит, когда значение mFinish становится равно true.

Взаимодействовать с потоком можно с помощью метода changeAction() (для смены вычитания на сложение и наоборот) и метода finish() (для завершения потока).

В объявлении переменных mIsIncrement и mFinish было использовано ключевое слово volatile (изменчивый, не постоянный). Его необходимо использовать для переменных, которые используются разными потоками. Это связано с тем, что значение переменной, объявленной без volatile, может кэшироваться отдельно для каждого потока, и значение из этого кэша может различаться для каждого из них. Объявление переменной с ключевым словом volatile отключает для неё такое кэширование и все запросы к переменной будут направляться непосредственно в память.

В этом примере показано, каким образом можно организовать взаимодействие между потоками. Однако есть одна проблема при таком подходе к завершению потока — Incremenator проверяет значение поля mFinish раз в секунду, поэтому может пройти до секунды времени между тем, когда будет выполнен метод finish(), и фактическим завершения потока. Было бы замечательно, если бы при получении сигнала извне, метод sleep() возвращал выполнение и поток незамедлительно начинал своё завершение. Для выполнения такого сценария существует встроенное средство оповещения потока, которое называется Interruption (прерывание, вмешательство).

Interruption

Класс Thread содержит в себе скрытое булево поле, подобное полю mFinish в программе Incremenator, которое называется флагом прерывания. Установить этот флаг можно вызвав метод interrupt() потока. Проверить же, установлен ли этот флаг, можно двумя способами. Первый способ — вызвать метод bool isInterrupted() объекта потока, второй — вызвать статический метод bool Thread.interrupted(). Первый метод возвращает состояние флага прерывания и оставляет этот флаг нетронутым. Второй метод возвращает состояние флага и сбрасывает его. Заметьте что Thread.interrupted() — статический метод класса Thread, и его вызов возвращает значение флага прерывания того потока, из которого он был вызван. Поэтому этот метод вызывается только изнутри потока и позволяет потоку проверить своё состояние прерывания.

Итак, вернемся к нашей программе. Механизм прерывания позволит нам решить проблему с засыпанием потока. У методов, приостанавливающих выполнение потока, таких как sleep(), wait() и join() есть одна особенность — если во время их выполнения будет вызван метод interrupt() этого потока, они, не дожидаясь конца времени ожидания, сгенерируют исключение InterruptedException.

Переделаем программу Incremenator – теперь вместо завершения потока с помощью метода finish() будем использовать стандартный метод interrupt(). А вместо проверки флага mFinish будем вызывать метод bool Thread.interrupted();
Так будет выглядеть класс Incremenator после добавления поддержки прерываний:

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

Заметьте что методы sleep() и join() обёрнуты в конструкции try-catch. Это необходимое условие работы этих методов. Вызывающий их код должен перехватывать исключение InterruptedException, которое они бросают при прерывании во время ожидания.

С запуском и завершением потоков разобрались, дальше я расскажу о методах, использующихся при работе с потоками.

Метод Thread.sleep()

Thread.sleep() — статический метод класса Thread, который приостанавливает выполнение потока, в котором он был вызван. Во время выполнения метода sleep() система перестает выделять потоку процессорное время, распределяя его между другими потоками. Метод sleep() может выполняться либо заданное кол-во времени (миллисекунды или наносекунды) либо до тех пор пока он не будет остановлен прерыванием (в этом случае он сгенерирует исключение InterruptedException).

Несмотря на то, что метод sleep() может принимать в качестве времени ожидания наносекунды, не стоит принимать это всерьез. Во многих системах время ожидания все равно округляется до миллисекунд а то и до их десятков.

Метод yield()

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

Метод join()

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

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

С помощью задания времени ожидания потока можно, например, выполнять обновление анимированной картинки пока главный (или любой другой) поток ждёт завершения побочного потока, выполняющего ресурсоёмкие операции:

В этом примере поток brain (мозг) думает над чем-то, и предполагается, что это занимает у него длительное время. Главный поток ждет его четверть секунды и, в случае, если этого времени на раздумье не хватило, обновляет «индикатор раздумий» (некоторая анимированная картинка). В итоге, во время раздумий, пользователь наблюдает на экране индикатор мыслительного процесса, что дает ему знать, что электронные мозги чем то заняты.

Приоритеты потоков

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

Работать с приоритетами потока можно с помощью двух функций:

void setPriority(int priority) – устанавливает приоритет потока.
Возможные значения priority — MIN_PRIORITY, NORM_PRIORITY и MAX_PRIORITY.

int getPriority() – получает приоритет потока.

Некоторые полезные методы класса Thread

Это практически всё. Напоследок приведу несколько полезных методов работы с потоками.

boolean isAlive() — возвращает true если myThready() выполняется и false если поток еще не был запущен или был завершен.

setName(String threadName) – Задает имя потока.
String getName() – Получает имя потока.
Имя потока – ассоциированная с ним строка, которая в некоторых случаях помогает понять, какой поток выполняет некоторое действие. Иногда это бывает полезным.

static Thread Thread.currentThread() — статический метод, возвращающий объект потока, в котором он был вызван.

long getId() – возвращает идентификатор потока. Идентификатор – уникальное число, присвоенное потоку.

Заключение

Отмечу, что в статье рассказано далеко не про все нюансы многопоточного программирования. И коду, приведенному в примерах, для полной корректности не хватает некоторых нюансов. В частности, в примерах не используется синхронизация. Синхронизация потоков — тема, не изучив которую, программировать правильные многопоточные приложения не получится. Почитать о ней вы можете, например, в книге «Java Concurrency in Practice» или здесь (всё на английском).

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *