В этой главе мы представим метод ожидания/пробуждения потока. Включаемое содержание включает в себя:
1. Введение в wait (), notify (), notifyall () и другие методы
2. Подождите () и уведомление ()
3. Подождите (длинный тайм -аут) и уведомление ()
4. WAIT () и notifyall ()
5. Почему уведомление (), wait () и другие функции, определенные в объекте, а не в потоке
Введение в wate (), notify (), notifyall () и другие методы
В Object.java определены интерфейсы, такие как wait (), notify () и notifyall (). Функция wait () состоит в том, чтобы позволить текущему потоку войти в состояние ожидания, и wait () также позволит текущему потоку выпустить блокировку, которую он содержит. Роль notify () и notifyall () состоит в том, чтобы разбудить поток ожидания на текущем объекте;
Подробная информация API о ожидании/пробуждении в классе объектов следующие:
notify () - разбудите один поток, ожидающий этого монитора объекта.
notifyall () - Разбудите все потоки, ожидающие этого монитора объекта.
wate () - поместите текущий поток в метод «Wait (блокировка)» и «до тех пор, пока другие потоки не вызовут метод notify () или notifyall () этого объекта», а текущий поток не будет пробужден (введен в « Готовное состояние »).
Подождите (длинный тайм -аут) - пусть текущий поток будет в «ожидании (блокирующем) состоянии» и «до тех пор, пока другие потоки не вызовут метод notify () или уведомление об этом объекте или не превышат указанное количество времени», и текущий поток пробужден (введите «Готово»).
Подождите (длинный тайм -аут, int nanos) - поместите текущий поток в состояние «wat (блокировка)», пока другой поток не назовет метод notify () или notifyall () этого объекта, или какой -либо другой поток прерывает текущий поток, или фактическое количество времени превысило », и текущий поток пробуждается (введено в« Готовное состояние »).
2. wate () и уведомление () примеры
Ниже приведен пример, чтобы продемонстрировать «wait () и уведомление () вместе».
Кода -копия выглядит следующим образом:
// waitTest.java исходный код
Class Threada Extends Thread {
public Threada (String name) {
супер (имя);
}
public void run () {
синхронизированный (это) {
System.out.println (thread.currentThread (). GetName ()+"call notify ()");
// Просыпайте текущую потоку ожидания
notify ();
}
}
}
открытый класс waitTest {
public static void main (string [] args) {
Threada T1 = new Threada ("T1");
синхронизированный (T1) {
пытаться {
// запустить "потока T1"
System.out.println (think.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 ();
}
}
}
}
Результаты работы:
Кода -копия выглядит следующим образом:
Главный старт T1
Основное ожидание ()
T1 Call notify ()
Главное продолжение
Результаты Описание:
Следующий рисунок иллюстрирует поток «основной поток» и «нить T1».
(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», «Замок текущего объекта» получается с помощью синхронизации (это); «главная нить».
(05) После завершения «потока T1» отпустите «Замок текущего объекта». Сразу же после этого «основная нить» приобретает «блокировку объекта T1», а затем работает.
Для приведенного выше кода? Друг однажды спросил: T1.Wait () должен сделать «Тейд T1», подождать;
Прежде чем ответить на этот вопрос, давайте посмотрим на абзац о ожидании в документе JDK:
Кода -копия выглядит следующим образом:
Приводит к тому, что текущий поток ждать, пока другой поток не вызовет метод notify () или метод notifyall () для этого объекта.
Другими словами, этот метод ведет себя точно так же, как если бы он просто выполняет вызов, ожидание (0).
Текущий поток должен владеть монитором этого объекта. может пересмотреть право собственности на монитор и возобновить исполнение.
Смысл на китайском языке примерно:
Приводит к тому, что «текущий поток» подождать, пока другой поток не вызовет () или уведомление (), чтобы разбудить поток. Другими словами, этот метод имеет тот же эффект, что и подожди (0)! (Дополнительный, для метода ожидания (Long Millis), когда Millis составляет 0, это означает бесконечное ожидание, пока он не разбудет notify () или notifyall ()).
Когда «текущий поток» вызывает wait (), он должен иметь блокировку синхронизации для объекта. После того, как поток вызовет wait (), блокировка будет выпущена; Затем поток продолжает ждать, пока она не повторно повторно не зарегистрирует «синхронизацию этого объекта», а затем он может продолжать работать.
Примечание. В объяснении JDK говорится, что функция wait () состоит в том, чтобы сделать «текущий поток», а «текущий поток» относится к потоке, работающему на процессоре!
Это также означает, что, хотя T1.Wait () - это метод wait (), называемый «потоком T1», место, где называется T1.Wait (), находится в «Main Thread Main». Основным потоком должен быть «текущий поток», то есть состояние, прежде чем T1.Wait () может быть выполнен. Следовательно, «текущий поток» в это время - это «основная поток»! Следовательно, T1.Wait () - это сделать «Основной поток», а не «поток T1»!
3. Подождите (длинный тайм -аут) и уведомление ()
подождите (длительный тайм -аут) поместит текущий поток в метод «ожидания (блокировки)» и «до тех пор, пока другие потоки не вызовут метод notify () или уведомление об этом объекте или превышат указанное количество времени», а также Текущий поток пробуждается (введено) «готово»).
В следующем примере демонстрируется тайм -аут ожидания (длительный тайм -аут), и поток пробуждается.
Кода -копия выглядит следующим образом:
// исходный код wallimeOuttest.java
Class Threada Extends Thread {
public Threada (String name) {
супер (имя);
}
public void run () {
System.out.println (think.currentThread (). GetName () + "run");
// порочный цикл, работающий непрерывно.
Пока (правда)
}
}
открытый класс waltimeOuttest {
public static void main (string [] args) {
Threada T1 = new Threada ("T1");
синхронизированный (T1) {
пытаться {
// запустить "потока T1"
System.out.println (think.currentThread (). GetName () + "start t1");
t1.start ();
// Основной поток ждет, пока T1 проснется через notify () или notifyall () или задержки, превышающие 3000 мс;
System.out.println (Thread.currentThread (). GetName () + "Вызовы wat");
T1.Wait (3000);
System.out.println (think.currentThread (). GetName () + "Продолжение");
} catch (прерванная экспрессия e) {
e.printstacktrace ();
}
}
}
}
Результаты работы:
Кода -копия выглядит следующим образом:
Главный старт T1
Основной звонок подождите
T1 Run // Примерно через 3 секунды ... вывод "Main продолжение"
Главное продолжение
Результаты Описание:
Следующий рисунок иллюстрирует поток «основной поток» и «нить T1».
(01) Обратите внимание, что «основной поток» на рисунке представляет основной поток ожидания, т. Е., Поток Main). «Поток T1» представляет собой нить T1, запустившись в Waithtest. И "Lock" представляет "синхронную блокировку объекта T1".
(02) Главный поток Main выполняет t1.start () для запуска «потока T1».
(03) Главный поток Main выполняет T1.Wait (3000), и в это время основной поток входит в «состояние блокировки». Необходимо «поток, используемый для блокировки объекта T1, чтобы разбудить его через notify () или notifyall ()» или «после 3000 мс тайм -аута», основной поток входит в «Готовное состояние», а затем может работать.
(04) После того, как «потока T1» работает, он входит в мертвую петлю и продолжает работать.
(05) После тайм -аута составляет 3000 мс, основной поток введет «Готовное состояние», а затем введет «состояние бега».
4. WAIT () и notifyall ()
Через предыдущий пример мы знаем, что notify () может разбудить один поток, ожидающий этого монитора объекта.
Ниже мы демонстрируем использование notifyall () через пример;
Кода -копия выглядит следующим образом:
открытый класс 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 ();
пытаться {
System.out.println (Thread.currentThread (). GetName ()+"Sleep (3000)");
Thread.sleep (3000);
} catch (прерванная экспрессия e) {
e.printstacktrace ();
}
синхронизированный (obj) {
// Основная тема ждет пробуждения.
System.out.println (think.currentThread (). GetName ()+"notifyall ()");
obj.notifyall ();
}
}
Static Class Threada Extends Thread {
public Threada (String name) {
супер (имя);
}
public void run () {
синхронизированный (obj) {
пытаться {
// Результат вывода вывода
System.out.println (think.currentThread (). GetName () + "wate");
// Просыпайте текущую потоку ожидания
obj.wait ();
// Результат вывода вывода
System.out.println (think.currentThread (). GetName () + "Продолжение");
} catch (прерванная экспрессия e) {
e.printstacktrace ();
}
}
}
}
}
Результаты работы:
Кода -копия выглядит следующим образом:
T1 Подождите
Основной сон (3000)
T3 Подождите
T2 Подождите
Основной notifyall ()
T2 продолжается
T3 продолжается
T1 продолжается
Результаты Описание:
Обратитесь к блок -схеме ниже.
(01) 3 потока «T1», «T2» и «T3» были созданы и запускались в главном потоке.
(02) Основная нить спит в течение 3 секунд через сон (3000). Во время основной нити в течение 3 секунд мы предполагаем, что три потока «T1», «T2» и «T3» работают. Возьмите «T1» в качестве примера. также будет ждать, пока другие потоки разбудят их через nofity () или nofityall ().
(03) Главная нить спит в течение 3 секунд, а затем работает. Выполните obj.notifyall (), чтобы разбудить ждающую потоку на OBJ, то есть разбудить три потока «T1», «T2» и «T3». Сразу после запуска синхронизации основного потока (OBJ) основной поток выпускает «Lock Lock». Таким образом, «T1», «T2» и «T3» могут получить «lock obj» и продолжать бежать!
5. Почему уведомление (), wait () и другие функции, определенные в объекте, а не в потоке
Такие функции, как wait (), notify () в объекте, как синхронизированный, будут работать на «блокировке синхронизации объектов».
WAIT () сделает «текущий поток». Не могу бежать!
ОК, после того, как поток вызовет wait (), он выпустит «синхронную блокировку», удерживаемое его замком; Теперь, пожалуйста, подумайте о вопросе: что такое уведомление () на основе пробуждения потока ожидания? Или, какова корреляция между wait () и notify ()? Ответ: на основе «блокировки синхронизации объектов».
Поток, отвечающий за пробуждение потока ожидания (мы называем его «поток пробуждения»), он получает только «синхронизационную блокировку объекта» (блокировка синхронизации здесь должна быть такой же, как и блокировка синхронизации потока ожидания), и вызовы notify () или после метода notifyall (), поток ожидания может быть пробужден. Несмотря на то, что ожидающий поток пробуждается; Вы должны подождать, пока поток пробуждения не выпустит «блокировку синхронизации объекта», прежде чем вы сможете получить «блокировку синхронизации объекта» и продолжить работу.
Короче говоря, notify (), wait () полагается на «синхронную блокировку», который удерживается блокировками объектов, и каждый объект имеет, и только один! Вот почему такие функции, как notify (), wait (), определены в классе объекта, а не в классе потока.