Диаграмма состояния потока
Поток включает в себя следующие 5 состояний.
1. Новое состояние: после создания объекта потока он входит в новое состояние. Например, Thread Think = new Thread ().
2. Готовное состояние (запускаемое): также известное как «исполняемое состояние». После создания объекта потока другие потоки вызывают метод start () объекта, чтобы запустить поток. Например, Thread.Start (). Поток в готовом состоянии может быть запланирован на процессоре в любое время.
3. Запуск состояния (запуск): поток получает разрешения на процессор для выполнения. Следует отметить, что потоки могут входить только в состояние бега только из состояния готового.
4. Заблокированное состояние: заблокированное состояние означает, что поток отдает права на использование процессора по какой -то причине и временно прекращается. Только когда поток не вступит в состояние готового состояния, у нее есть шанс перейти в состояние бега. Есть три типа блокировки:
(01) Ожидание, чтобы заблокировать - позвонив по методу wait () потока, позвольте потоку ждать завершения определенной работы.
(02) Синхронизированное блокирование-нить не может получить синхронизированную блокировку синхронизации (поскольку блокировка занята другими потоками), он введет синхронизированное состояние блокировки.
(03) Другое блокировка-поток введет состояние блокировки, вызывая Sleep () или Join () потока или выпустив запрос ввода/вывода. Когда состояние Sleep () истекло, join () ждал, пока поток заканчивается или истекло, или обработка ввода-вывода была завершена, поток снова въехал в состояние готового состояния.
5. Мертвое состояние: поток завершил выполнение или выход метода run () из -за исключения, и поток заканчивает свой жизненный цикл.
Реализовать поток методов многопоточного
Поток: Уточните класс потока, реализуйте метод запуска и вызовите метод запуска в основной функции, чтобы запустить поток
Запустить: интерфейс, реализует выполняемый интерфейс, передает его как параметр конструктора потока и вызывает метод начала в основном
пример:
Задача класса реализует runnable {private int ticket = 10; @Override public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println (Thread.currentThread (). GetName () + "Проданный билет" + this.ticket--); }}}}}}; public class RunnableTest {public static void main (string [] args) {task myTask = new Task (); Потока T1 = новый поток (MyTask); Потока T2 = новый поток (MyTask); Потока T3 = новый поток (MyTask); t1.start (); t2.start (); t3.start (); }} // threadtest.java Исходной код класс MyThread Extends Thread {private int Ticket = 10; public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println (this.getName () + "Продажа билета: билет" + this.ticket--); }} / Каждая поток продает по 10 билетов каждый! Mythread T1 = new Mythread (); Mythread t2 = new Mythread (); Mythread T3 = New Mythread (); t1.start (); t2.start (); t3.start (); }};
Разница между потоком и бегством
Поток - это класс, а запускается интерфейс; Сам поток - это класс, который реализует запускаемый интерфейс. Мы знаем, что «класс может иметь только один родительский класс, но он может реализовать несколько интерфейсов», так что работает лучше масштабируемость. Кроме того, Runnable также можно использовать для «совместного использования ресурсов». То есть несколько потоков создаются на основе определенного запускаемого объекта, и они будут делиться ресурсами на выполняемом объекте. Как правило, рекомендуется реализовать многопоточное с помощью «Runnable»!
Thread's Run and Spart
start (): его функция - запустить новый поток, а новый поток выполнит соответствующий метод run (). start () не может быть вызван неоднократно. start () фактически запускает поток через локальный метод start0 (). start0 () запустит новый поток, а новый поток вызовет метод run ().
run (): run () можно называть многократно, как обычные методы участника. Если вы вызовите run () отдельно, run () будет выполнен в текущем потоке, а новый поток не будет запущен! run () должен напрямую вызовать метод run () запускаемого члена потока потока и не будет создавать новый поток.
// demo.java's Source Code Class Mythread Extends Thread {public Mythread (String name) {super (name); } public void run () {System.out.println (thread.currentThread (). getName ()+"работает"); }}; открытый класс демонстрация {public static void main (string [] args) {thread mythread = new Mythread ("mythread"); System.out.println (thread.currentThread (). GetName ()+"Call Mythread.run ()"); mythread.run (); System.out.println (thread.currentThread (). GetName ()+"Call Mythread.start ()"); mythread.start (); }}Выход:
Главный звонок mythread.run () main - это running -main call mithread.start () Mythread работает
синхронизированный
В Java каждый объект имеет блокировку синхронизации. Когда мы называем синхронизированный метод объекта, приобретается блокировка объекта, и синхронизированный (OBJ) приобретает блокировку синхронизации «объекта OBJ». Различные потоки доступа к блокировке синхронизации являются взаимоисключающими. Замок синхронизации объекта может быть получен только одним потоком в определенное время. Через блокировку синхронизации мы можем достичь взаимоисключающего доступа к «объекту/методу» в нескольких потоках. Например, теперь есть два потока A и поток B, которые будут доступны к «синхронизированной блокировке объекта obj». Предположим, что в какой -то момент резьба А приобретает «блокировку синхронизации OBJ» и выполняет некоторые операции; В настоящее время нить B также пытается получить «блокировку синхронизации OBJ» - нить B не может приобрести, он должен подождать, пока нить не выпустит «блокировку синхронизации OBJ» и может быть запущено.
Основные правила
Статья 1: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки будут заблокированы от доступа к «синхронизированному методу» или «синхронизированный кодовый блок» «объекта».
Статья 2: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки все равно могут получить доступ к асинхронизированному кодовому блоку «этот объект».
Статья 3: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки будут заблокированы от доступа к другим «синхронизированным методам» или «синхронизированным кодовым блоком» «объекта».
Синхронизированный метод
public Synchronized void foo1 () {System.out.println ("Синхронизированный метод");} синхронизированный кодовый блок public void foo2 () {synchronized (this) {System.out.println ("синхронизированный метод"); }}Это в синхронном блоке кода относится к текущему объекту. Это также может быть заменено другими объектами, такими как это заменяется OBJ, затем FOO2 () приобретает блокировку синхронизации OBJ при синхронизации (OBJ).
Синхронизированные кодовые блоки могут более точно контролировать области доступа к конфликту, а иногда и более эффективно выполнять
Блокировка экземпляра и глобальная блокировка
Замок экземпляра-заблокирован на объекте экземпляра. Если класс является синглтоном, то замок также имеет концепцию глобального блокировки. Синхронизированное ключевое слово соответствует блокировке экземпляра.
Global Lock- этот замок нацелен на класс. Независимо от того, сколько объектов экземпляр, потоки делятся блокировкой. Глобальная блокировка соответствует статическому синхронизированию (или заблокированной на объекте класса или класса загрузчика этого класса).
Pulbic Class Something {public synchronized void issynca () {} public synchronized void issyncb () {} public static synchronized void csynca () {} public static synchronized void csyncb () {}}
(01) x.issynca () и x.issyncb () нельзя получить одновременно. Потому что issynca () и issyncb () являются блокировками синхронизации, которые получают доступ к одному и тому же объекту (объект x)!
(02) x.issynca () и y.issynca () можно получить одновременно. Поскольку он не доступ к блокировке синхронизации одного и того же объекта, x.issynca () обращается к блокировке синхронизации x, в то время как y.issynca () обращается к блокировке синхронизации y.
(03) x.csynca () и y.csyncb () нельзя получить одновременно. Поскольку csynca () и csyncb () являются статическими типами, x.csynca () эквивалентен чем -то. Issynca (), а y.csyncb () эквивалентен чем -то.
(04) x.issynca () и что -то.csynca () можно получить одновременно. Поскольку issynca () является методом экземпляра, x.issynca () использует блокировку объекта x; В то время как csynca () является статическим методом, что -то. Csynca () может понять, что это используется «блокировка класса». Следовательно, к ним можно получить доступ одновременно.
Блокировка потока и пробуждение ожидание, уведомление, уведомление
В Object.java определены интерфейсы, такие как wait (), notify () и notifyall (). Функция wait () состоит в том, чтобы позволить текущему потоку войти в состояние ожидания, и wait () также позволит текущему потоку выпустить блокировку, которую он содержит. Роль notify () и notifyall () состоит в том, чтобы разбудить резьбу для ожидания на текущем объекте; notify () - это разбуждение одного потока, в то время как notifyall () - это разбудить все потоки.
Подробная информация API о ожидании/пробуждении в классе объектов следующие:
notify () - разбудите один поток, ожидающий этого монитора объекта.
notifyall () - Разбудите все потоки, ожидающие этого монитора объекта.
WAIT () - Поместите текущий поток в метод «WAIT (блокировка)» и «до тех пор, пока другие потоки не вызовут метод notify () или notifyall () этого объекта», а текущий поток не будет пробужден (введено в «Готовное состояние»).
Подождите (длинный тайм -аут) - поместите текущий поток в метод «ожидание (блокировки)» и «до тех пор, пока другие потоки не вызовут метод obhent () или уведомление об уведомлении () или не превышают указанное количество времени», и текущий поток пробуждается (введено в «Готовное состояние»).
Подождите (Long Timeout, int nanos) - пусть текущий поток будет в методе «ожидание (блокировки)», пока другие потоки не вызовут метод объекта () notify () или уведомление (), или какой -либо другой потоковой сигнал не прерывает текущий поток или превышает определенное количество времени », и текущий поток разбудится (введено в« Готовное состояние »).
// wabtTest.java's Source Code Class Threada Extends Thread {public Threata (String name) {super (name); } public void run () {synchronized (this) {System.out.println (thread.currentThread (). getName ()+"call notify ()"); // Пробуждение текущего потока ожидания notify (); }}} public class waitTest {public static void main (string [] args) {threada t1 = new threada ("t1"); Synchronized (t1) {try {// start "Thread T1" System.out.println (thread.currentThread (). getName ()+"start t1"); t1.start (); // Основной поток ждет, пока T1 проснется через notify (). System.out.println (think.currentThread (). GetName ()+"wait ()"); t1.wait (); System.out.println (think.currentThread (). GetName ()+"Продолжение"); } catch (прерванное искусство e) {e.printstacktrace (); }}}}Выход
Главный старт t1main wait () t1 call notify () main contin
(01) Обратите внимание, что «основной поток» на рисунке представляет «Главный поток». «Поток T1» представляет «потока T1», запустившись в WaitStest. И "Lock" представляет "синхронную блокировку объекта T1".
(02) «Основной поток» создает новый «потока T1» через New Threada («T1»). Затем «синхронная блокировка объекта T1» получается посредством синхронизации (T1). Затем позвоните T1.Start (), чтобы запустить «Поток T1».
(03) «Основной поток» выполняет t1.wait (), чтобы выпустить «блокировку объекта T1» и входит в «ожидание (блокировка) состояние». Подождите, пока потоки на объектах T1 разбудят его через notify () или notifyall ().
(04) После запуска «потока T1» получается «блокировка текущего объекта» через синхронизированный (это); Затем вызовите notify (), чтобы разбудить «Поток ожидания на текущем объекте», то есть разбудить «Главный поток».
(05) После завершения «потока T1» отпустите «Замок текущего объекта». Сразу же после этого «основная нить» приобретает «блокировку объекта T1», а затем работает.
T1.Wait () - это метод wait (), который называется «потока T1», но место, где называется T1.Wait (), находится в «Main Thread Main». Основным потоком должен быть «текущий поток», то есть состояние, прежде чем T1.Wait () может быть выполнен. Следовательно, «текущий поток» в это время - это «основная поток»! Следовательно, T1.Wait () - это сделать «Основной поток», а не «поток T1»!
пакет Thread.test; открытый класс notifyalltest {частный статический объект obj = new Object (); public static void main (string [] args) {threada t1 = new threada ("t1"); Threada T2 = new Threada ("T2"); Threada T3 = New Threada ("T3"); t1.start (); t2.start (); t3.start (); try {System.out.println (thread.currentThread (). getName ()+"Sleep (3000)"); Thread.sleep (3000); } catch (прерванное искусство e) {e.printstacktrace (); } synchronized (obj) {System.out.println (thread.currentThread (). getName ()+"notifyall ()"); obj.notifyall (); // Wake Up t1.t2.t3 здесь}} static class threada extends {public Threada (String name) {super (name); } public void run () {synchronized (obj) {try {// System Result System.out.out.println (thread.currentThread (). getName () + "wate"); // Выпустить блокировку объекта obj obj.wait (); // System System.out.out.println (thread.currentThread (). GetName () + "Продолжение"); } catch (прерванное искусство e) {e.printstacktrace (); }}}}}} Выход:
T1 Waitmain Sleep (3000) T3 waitt2 waitmain notifyall () t2 contina3 Продолжение1
(01) 3 потока «T1», «T2» и «T3» были созданы и запускались в главном потоке.
(02) Основная нить спит в течение 3 секунд через сон (3000). Во время основной нити в течение 3 секунд мы предполагаем, что три потока «T1», «T2» и «T3» работают. Возьмите «T1» в качестве примера. Когда он работает, он выполнит obj.wait (), чтобы ждать, пока другие потоки разбудили его через notify () или nofityall (); Точно так же, «T2» и «T3» также будут ждать, пока другие потоки разбудят их через nofity () или nofityall ().
(03) Главная нить спит в течение 3 секунд, а затем работает. Выполните obj.notifyall (), чтобы разбудить ждающую потоку на OBJ, то есть разбудить три потока «T1», «T2» и «T3». Сразу после запуска синхронизации основного потока (OBJ) основной поток выпускает «Lock Lock». Таким образом, «T1», «T2» и «T3» могут получить «lock obj» и продолжать бежать!
Уведомлять, уведомлять и блокировать отношения
Такие функции, как wait (), notify () в объекте, как синхронизированный, будут работать на «блокировке синхронизации объектов».
WAIT () заставит «текущий поток» ждать. Поскольку поток попадает в состояние ожидания, поток должен выпустить «синхронную блокировку», удерживаемую его блокировкой, иначе другие потоки не смогут получить «синхронную блокировку» и не смогут запустить!
ОК, после того, как поток вызовет wait (), он выпустит «синхронную блокировку», удерживаемую его блокировкой; и, согласно предыдущему введению, мы знаем, что ожидающий поток может быть пробужден Notify () или notifyall (). Теперь, пожалуйста, подумайте о вопросе: что такое уведомление () на основе пробуждения потока ожидания? Или, какова корреляция между wait () и notify ()? Ответ: на основе «блокировки синхронизации объектов».
Поток, отвечающий за пробуждение потока ожидания (мы называем его «поток пробуждения»), может разбудить поток ожидания только после получения «синхронной блокировки этого объекта» (блокировка синхронизации здесь должна быть такой же, как и блокировку синхронизации потока ожидания) и вызов методов notify () или уведомление (). Хотя ожидающая нить пробуждена; Тем не менее, это не может быть выполнено немедленно, потому что поток пробуждения по-прежнему держит «синхронную блокировку для объекта». Вы должны подождать, пока поток пробуждения не выпустит «блокировку синхронизации объекта», прежде чем вы сможете получить «блокировку синхронизации объекта» и продолжить работу.
Короче говоря, notify (), wait () полагается на «синхронную блокировку», который удерживается блокировками объектов, и каждый объект имеет, и только один! Вот почему такие функции, как notify (), wait (), определены в классе объекта, а не в классе потока.
Уступки потока
Уступки потока заставляют изменение потока от состояния выполнения в состояние готового состояния, чтобы другие потоки ожидания с тем же приоритетом могли получить права на выполнение; Тем не менее, не гарантируется, что после текущих вызовов потока доход () другие потоки с тем же приоритетом определенно получат права исполнения; Также возможно, что текущий поток входит в «состояние бега» и продолжает работать.
уступить и подождать
(01) WAIT () - это позволить потоке войти в состояние «ожидание (блокировка)» из «управляемого состояния», в то время как не hield () - это позволить потоку войти в «Готовное состояние» из «управляемого состояния».
(02) WAIT () - это блокировка синхронизации, которая будет отключить объект, который он удерживает, в то время как метод hellive () не будет выпускать блокировку.
(03) Подождите метод объекта, выход - метод потока
Нить сон
Функция Sleep () состоит в том, чтобы позволить нынешнему потоку спать, то есть текущий поток будет входить из «состояния бега» в состояние «сон (блокировка)». Sleep () будет указывать время сна, а время сна нить будет больше, чем/равно времени сна; Когда поток снова разбудится, она изменится от «состояния блокировки» на «готовое состояние», ожидая, когда запланирован на ЦП.
Разница между сна и ожиданием
Функция wait () состоит в том, чтобы позволить текущему потоку входить в состояние «ожидания (блокировки) из« управляемого состояния », а также освободить блокировку синхронизации. Функция Sleep () состоит в том, чтобы позволить нынешнему потоку войти в состояние« блокировки) из «работающего состояния». (На самом деле это не сильно отличается)
wate () выпускает блокировку синхронизации объекта, в то время как Sleep () не выпускает блокировку
подождите метод объекта, сон - это метод потока
Присоединиться
Позвольте основной потоке подождать, и детская нить может продолжать работать после завершения основного потока.
прерывать
Используется для прекращения блокированного потока
@OverridePublic void run () {try {while (true) {// выполнить задачу ...}} catch (прерывание EXCEPTION IE) {// Из -за исключения прерывания, выйти из цикла (true) и поток заканчивается! }}В то время как (true) призыв к Enterrupt () потока генерирует прерывание прерывания. Прерывавший захват находится на улице, в то время как (правда), тем самым выходя из петли (истинного)
Прекращает поток в состоянии управления
@OverridePublic void run () {while (! IsErengreting ()) {// Выполнить задачу ...}}Общий способ прекратить потоки
@OverridePublic void run () {try {// 1. isErengrupted () гарантирует, что поток будет прекращен, если прерывание помечено true. while (! isErengeTrupted ()) {// Выполнить задачу ...}} catch (прерывание Exception IE) {// 2. Исключение прерывания. }}
Приоритет потока
Диапазон приоритетов потока в Java составляет от 1 до 10, а приоритет по умолчанию составляет 5. В Java есть два типа потоков: Пользовательский поток и потока демона. Их можно отличить методом isdaemon (): если false возвращается, это означает, что поток - это «пользовательский поток»; В противном случае это «нить демон». Пользовательские потоки обычно выполняют задачи на уровне пользователя, в то время как потоки демона также являются «бэкэнд-потоками», которые обычно используются для выполнения фоновых задач. Следует отметить, что виртуальная машина Java выйдет после завершения «Пользовательского потока».
Каждый поток имеет приоритет. «Высокие приоритетные потоки» будут предшествовать выполнению по сравнению с «потоками с низким приоритетом». Каждый поток может быть помечен как демон или не-демон. При создании нового дочернего потока в каком -то используемом основном потоке приоритет дочернего потока устанавливается на равнный «приоритет основного потока, который его создал», и «Детский поток будет веткой демона», когда и только если «основной поток, который создал его, является потоком демона».
Когда запускается виртуальная машина Java, обычно существует одна нить не-дамон (этот поток запускается с помощью метода Main ()). JVM будет работать до тех пор, пока не произойдет какое -либо из следующих условий, и JVM прекратит пробег:
(01) метод exit () вызывается, и exit () имеет разрешение на выполнение нормально.
(02) Все «нить не-дамон» мертвы (то есть в JVM есть только «потоки демон»).
Демон
(01) Основной поток - это пользовательский поток, а дочерняя поток T1, который он создает, также является пользовательским потоком.
(02) T2 - нить демон. Когда выполняются «основной поток Main» и «Подпоточный T1» (они оба пользовательские потоки), и остается только поток Daemon T2, JVM автоматически выходит.
Производители и потребительские проблемы
(01) Производитель производит только тогда, когда склад не заполнен, и останавливает производство, когда склад заполнен.
(02) Потребители могут потреблять только тогда, когда у них есть продукты в хранении, и подождать, если у них есть пустые склады.
(03) Когда потребители обнаруживают, что на складе нет продукта, они уведомит производителя.
(04) Когда производители производят расходные продукты, они должны уведомить потребителей ожидания.
Связь между потоками
Метод: общая память и обмен сообщениями
Общая память: поток A и поток B Общая память, потока A обновляет значение общей переменной, обновляет ее в основную память, а поток B переходит к основной памяти, чтобы прочитать обновленные переменные потока A. Весь процесс связи должен пройти через основную память. Синхронизация выполняется явно.
Если переменная имеет летучую тип, чтение и запись переменной будет атомной. Если это многочисленные летучие операции или композитные операции, аналогичные летучим ++, эти операции не являются атомными полностью.
Сама летучая переменная имеет следующие характеристики:
[Видимость]: при чтении летучей переменной вы всегда можете увидеть последнюю запись в летучую переменную (любой поток).
[Атомность]: он обладает атомацией для чтения/записи любой единой летучих переменной, но у нее нет атомности для составных операций, аналогичных летучим ++.
летучая запись: при написании летучивой переменной JMM будет промывать общую переменную в локальной памяти, соответствующую потоку, с основной памятью.
Прямое чтение: при чтении изменчивой переменной JMM лишает аннулирования локальной памяти, соответствующей потоке. Затем поток будет считывать общую переменную из основной памяти.
Доставка сообщения: отправка сообщения выполняется неявно до принятия сообщения.
Threadlocal
Threadlocal не используется для решения проблемы многопоточного доступа к общим объектам. Вообще говоря, объект в потоке через Threadlocal.set () является объектом, используемым самим потоком. Другие потоки не должны быть доступны и не могут быть доступны. Threadlocal позволяет каждому потоку поддерживать свой собственный независимый объект. Он не реализуется через Threadlocal.set (), а объект, созданный операцией нового объекта в каждом потоке. Каждый поток создает один, а не копию или копию объекта. Ссылка на недавно созданный объект сохраняется на собственной карте каждого потока через Threadlocal.set (). Каждый поток имеет такую карту. Когда выполняется Threadlocal.get (), каждый поток вынимает объект, размещенный с его собственной карты. Следовательно, то, что вынимает, является объектом в каждом потоке. В качестве клавиши карты используется резервный экземпляр. Если то, что Threadlocal.set () входит, - это один и тот же объект, общий с несколькими потоками, то Threadlocal.get () нескольких потоков по -прежнему получает сам общий объект, и все еще существует задача параллельного доступа.
Реализация нитолокальных
импортировать java.util.collections; импортировать java.util.hashmap; импортировать java.util.map; /** * класс с использованием Threadlocal * * @author Leizhimin 2010-1-5 10:35:27 */public class mythreadlocal {// Определите трентокальную переменную для сохранения int или integer data private com.lavasoft.test2.threadlocal <Inceger> tl = com.lavasoft.test2.threadlocal <Integer> tl = lavasoft.test2.thread> () Integer initialValue () {return 0; }}; public integer getNextnum () {// Получить значение TL и добавить 1 и обновить значение t1 tl.set (tl.get () + 1); вернуть tl.get (); }} класс Threadlocal <T> {Private Map <Thread, t> map = collections.synchronizedmap (new hashmap <thread, t> ()); public ThinkLocal () {} защищенный t initialValue () {return null; } public t get () {thread t = thread.currentThread (); T obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } вернуть obj; } public void set (t value) {map.put (thread.currentThread (), value); } public void remove () {map.remove (thread.currentThread ()); }}На самом деле, Threadlocal делает это:
public t get () {Thread t = Thread.currentThread (); Threadlocalmap map = getMap (t); if (map! = null) {threadlocalmap.Entry e = map.getEntry (this); if (e! = null) return (t) e.value; } return setInitialValue (); }