Это внимательное примечание о заполнении ям. За годы самостоятельного изучения Java я всегда изучал новые технологии. По пути я обнаружил, что наступил на бесчисленное количество ям, но ям всего несколько заполнен. Внезапно я обнаружил, что иногда это действительно не вопрос многолетнего опыта работы, даже если вы проработали десять лет, некоторые вещи просто 10-летняя дыра, если вы не учились усердно.
Когда я впервые столкнулся с многопоточностью, я знал, что существует функция ожидания/пробуждения. Я написал демонстрацию и больше никогда ее не читал. Что же касается того, что это такое или какие проблемы он может решить, люди и я одинаково неоднозначны. На этот раз автор попытается познакомить вас с механизмом ожидания/пробуждения.Прочитав эту статью, вы получите следующие моменты:
- Какие проблемы приносит циклическое ожидание
- Оптимизируйте циклическое ожидание с помощью механизма ожидания-пробуждения
- Игнорируемые детали в механизме ожидания пробуждения
Во-первых, циклическая задача ожидания
Если предположить, что сегодня должна быть выплачена зарплата, а сильный начальник собирается хорошо поесть, то весь обеденный процесс можно разделить на следующие этапы:
- приказ
- Окно в ожидании еды
- столовая
public static void main(String[] args) {
// 是否还有包子
AtomicBoolean hasBun = new AtomicBoolean();
// 包子铺老板
new Thread(() -> {
try {
// 一直循环查看是否还有包子
while (true) {
if (hasBun.get()) {
System.out.println("老板:检查一下是否还剩下包子...");
Thread.sleep(3000);
} else {
System.out.println("老板:没有包子了, 马上开始制作...");
Thread.sleep(1000);
System.out.println("老板:包子出锅咯....");
hasBun.set(true);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
System.out.println("小强:我要买包子...");
try {
// 每隔一段时间询问是否完成
while (!hasBun.get()) {
System.out.println("小强:包子咋还没做好呢~");
Thread.sleep(3000);
}
System.out.println("小强:终于吃上包子了....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
В приведенном выше коде есть большая проблема, то есть начальнику нужно постоянно проверять, есть ли еще плюшки, а заказчику нужно время от времени навещать начальника, что явно неразумно, это типичное циклическое ожидание проблема.
Код для такого рода проблем обычно представляет собой следующий шаблон:
while (条件不满足) {
Thread.sleep(3000);
}
doSomething();
В соответствии с компьютером выявляется проблема:Непрерывно используйте механизм опроса, чтобы определить, выполняются ли условия. Если время опроса слишком мало, ресурсы ЦП будут потрачены впустую. Если интервал слишком велик, требуемые ресурсы не могут быть получены вовремя..
Во-вторых, механизм ожидания/пробуждения
заРешите проблему циклического ожидания загрузки ЦП и своевременности информации., Java предоставляет механизм ожидания пробуждения. С точки зрения непрофессионала этоот активного к пассивному, Когда условие установлено, соответствующий поток активно уведомляется, а не запрашивает сам поток.
2.1 Основные понятия
Механизм ожидания/пробуждения, также называемый ожиданием уведомления (я предпочитаю называть его пробуждением, а не уведомлением), означает, что поток A вызывает метод wait() объекта O, чтобы перейти в состояние ожидания, а другой поток вызывает метод O notify(). или notifyAll() поток A возвращается из метода wait() объекта O после получения уведомления, а затем выполняет последующие операции.
Процесс апелляции заключается в общении между потоком А и потоком В через объект О. После вызова метода wait() объекта О в потоке поток переходит в состояние блокировки на длительное время, а в других потоках объект О вызывает notify() или При использовании метода notifyAll будет пробужден соответствующий заблокированный поток.
2.2 Базовый API
Методы, связанные с механизмом ожидания/пробуждения, доступны для любого объекта Java, поскольку эти методы определены в объекте суперкласса всех объектов Java.
notify: уведомить поток, ожидающий возврата объекта из метода wait(), и предпосылкой возврата является то, что поток получает блокировку объекта.
notifyAll: уведомить все потоки, ожидающие этого объекта
wait: Поток, вызывающий этот метод, входит в состояние ожидания блокировки, ожидая только ответа другого потока.уведомлен или прерванвернется, звонитеМетод ожидания снимает блокировку с объекта.
wait(long): после ожидания в течение более чем периода времени без пробуждения тайм-аут автоматически возвращается в миллисекундах.
2.3 Оптимизация циклического ожидания с помощью механизма ожидания пробуждения
public static void main(String[] args) {
// 是否还有包子
AtomicBoolean hasBun = new AtomicBoolean();
// 锁对象
Object lockObject = new Object();
// 包子铺老板
new Thread(() -> {
try {
while (true) {
synchronized (lockObject) {
if (hasBun.get()) {
System.out.println("老板:包子够卖了,打一把王者荣耀");
lockObject.wait();
} else {
System.out.println("老板:没有包子了, 马上开始制作...");
Thread.sleep(3000);
System.out.println("老板:包子出锅咯....");
hasBun.set(true);
// 通知等待的食客
lockObject.notifyAll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
System.out.println("小强:我要买包子...");
try {
synchronized (lockObject) {
if (!hasBun.get()) {
System.out.println("小强:看一下有没有做好, 看公众号cruder有没有新文章");
lockObject.wait();
} else {
System.out.println("小强:包子终于做好了,我要吃光它们....");
hasBun.set(false);
lockObject.notifyAll();
System.out.println("小强:一口气把店里包子吃光了, 快快乐乐去板砖了~~");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Вышеописанный процесс сокращает операции опроса и проверки, а после того, как поток вызовет метод wait(), блокировка будет снята без потребления ресурсов ЦП, тем самым улучшив производительность программы.
В-третьих, основная парадигма механизма ожидания пробуждения.
Ожидание и пробуждение — это одно из средств межпотокового взаимодействия, которое используется для координации нескольких потоков для работы с одним и тем же источником данных. В практических приложениях это обычно используется для оптимизации проблемы циклического ожидания.Для ожидающей стороны и уведомляющей стороны можно выделить следующую классическую парадигму.
Следует отметить, что в логике, выполняемой ожидающей стороной, цикл while должен использоваться для оценки условия ожидания, поскольку выполнениеМетод notify/notifyAll позволяет только ожидающему потоку вернуться из метода ожидания вместо повторного входа в критическую секцию.
/**
* 等待方执行的逻辑
* 1. 获取对象的锁
* 2. 检查条件,如果条件不满足,调用对象的wait方法,被通知后重新检查条件
* 3. 条件满足则执行对应的逻辑
*/
synchronized(对象){
while(条件不满足){
对象.wait()
}
doSomething();
}
/**
* !! 通知方执行的逻辑
* 1. 获取对象的锁
* 2. 改变条件
* 3. 通知(所有)等待在对象上的线程
*/
synchronized(对象){
条件改变
对象.notify();
}
Эта парадигма программирования обычно предназначена для типичного уведомителя и официанта, а иногда обе стороны могут иметь двойную идентичность, даже если официант также является уведомителем, как в нашем случае выше.
В-четвертых, notify/notifyAll не снимает блокировку
Думаю, что половина инженеров не знает об этой проблеме **При выполнении метода wait() блокировка снимается автоматически, но после выполнения метода notify() блокировка не снимается, а синхронизированный код, в котором выполняется метод notify(), должен быть выполнен, блок будет освобожден. **Это очень важно, и многие инженеры склонны игнорировать это.
lockObject.notifyAll();
System.out.println("小强:一口气把店里包子吃光了, 快快乐乐去板砖了~~");
В коде кейса намеренно установлено сначала notifyAll, а затем печать; результаты на рисунке выше также подтверждают наше описание, и заинтересованные друзья могут выполнить код кейса вручную.
Пять, ожидание и пробуждение должны сначала получить замок
в парадигме программирования ожидания-бодрствованияМетоды wait, notify, notifyAll часто нельзя вызывать напрямую, и их необходимо выполнять в критической секции после получения блокировки.
И может только разбудить потоки, ожидающие одной и той же блокировки.
Когда поток вызывает метод ожидания, он добавляется в очередь ожидания.При выполнении уведомления пробуждается первый ожидающий поток в очереди (поток с наибольшим временем ожидания), а при вызове метода notifyAll , все ожидающие потоки в ожидающем потоке будут пробуждены..
Шесть, сон не снимает блокировку, а ожидание отпускает
В процессе оптимизации циклического ожидания с помощью механизма пробуждения ожиданием важной особенностью является то, что исходный метод sleep() заменяется методом wait().Самая большая разница между ними заключается в том, чтоМетод ожидания снимает блокировку, в то время как сон не, кроме того, есть важное отличие,sleep — это метод Thread, который может выполняться где угодно, а wait — это метод объекта Object, который должен выполняться в синхронизированном блоке кода..