undefined

Другие детали синхронизации и многонитиевости

Java Multithreading
7 уровень , 10 лекция
Доступна

— Привет, Амиго!

Есть такая здоровенная тема, называется Java Memory Model. В принципе знать ее тебе пока не обязательно, но услышать про это – будет полезно.

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

— И сложнее!

— Да, лучше и сложнее. Это как самолет. Летать на самолете лучше, чем идти пешком, но сложнее. Попробую объяснить тебе новую ситуацию очень упрощенно.

Вот что было придумано. В код был добавлен механизм синхронизации локальной памяти нитей, названный «happens before» (дословно «случилось перед»). Был придуман ряд правил/условий, при наступлении которых память синхронизируется – обновляется до актуального состояния.

Пример:

Порядок Нить 1 Нить 2
1
2

101
102
103
104
105

201
202
203
204
205
public int y = 1;
public int x = 1;

x=2;
synchronized(mutex)
{
 y = 2;
}
нить ждет освобождения мютекса — mutex

synchronized(mutex)
{
 if (y == x)
 System.out.println("YES");
}

Одно из таких условий – это захват освобожденного мютекса. Если мютекс был освобожден и снова захвачен, то перед захватом обязательно выполнится синхронизация памяти. Нить 2 увидит «самые новые» значения переменных x и y, даже если не объявлять их volatile.

— Как интересно. И много таких условий?

— Достаточно, вот некоторые условия синхронизации памяти:

  • В рамках одной нити любая команда happens-before (читается «случается перед») любой операцией, следующей за ней в исходном коде.
  • Освобождение лока (unlock) happens-before захватом того же лока (lock).
  • Выход из synchronized блока/метода happens-before вход в synchronized блок/метод на том же мониторе.
  • Запись volatile поля happens-before чтение того же самого volatile поля.
  • Завершение метода run экземпляра класса Thread happens-before выход из метода join() или возвращение false методом isAlive() экземпляром той же нити.
  • Вызов метода start() экземпляра класса Thread happens-before начало метода run() экземпляра той же нити.
  • Завершение конструктора happens-before начало метода finalize() этого класса
  • Вызов метода interrupt() на нити happens-before, когда нить обнаружила, что данный метод был вызван, либо путем выбрасывания исключения InterruptedException, либо с помощью методов isInterrupted() или interrupted()

— Т.е. все немного сложнее, чем я думал?

— Да, немного сложнее…

— Спасибо, Риша, буду думать.

— Не заморачивайся сильно на эту тему. Придет время, сам все поймешь. Пока тебе лучше разбираться в основах, чем лезть в дебри внутреннего устройства Java-машины.

— О_о. М-да. Некоторые вещи лучше не знать.

Комментарии (45)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Regina Kazan Уровень 36 Казань Россия
15 февраля 2021
более менее мне помогли понять эту тему вот эти 2 ресурса: ссылка 1 ссылка 2
Vadim Уровень 35
21 января 2021
Вчера вечером решил остановиться на этой теме, подумал, что тема сложная и могу ничего не понять. Сегодня со свежей головой все равно толком ничего не понял. Выходит работает правило: " не оставляй на завтра то, что можешь сделать сегодня" Мог еще вчера быть на следующей лекции))
Valua Sinicyn Уровень 41 Харьков Украина
8 января 2021
Фуф... Как все запущено © Вот 4 основных правила happens before простым языком: • освобождение mutex объекта одним потоком, происходит раньше захвата mutex объекта другим потоком; • метод start() выполняется раньше метода run(); • завершение метода run() происходит раньше выхода из метода join(); • запись в volatile поля long или double происходит раньше чтения из них.
Kex Уровень 38 Тольятти Expert
1 июня 2020
https://www.youtube.com/watch?v=iqatGX1HmPo обязательно гляньте видео по теме
Владислав Уровень 41 Санкт-Петербург Россия
22 мая 2020
Как по мне, так было бы более не логично, если бы описанные условия происходили как-то по-другому, чем это описано. Логично, что метод interrupt() вызовется раньше, чем это обнаружит метод, или то, что команда, идущая в коде следующей, не выполнится раньше предыдущей. А так, все логично, вроде бы, все в рамках ООП) Или я не так понял условия?)
Андрей Уровень 41 Одесса
20 ноября 2019
Из книги java concurrency in practice: ... Чтобы гарантировать, что поток, выполняющий действие B, сможет видеть результаты действия A (независимо от того, происходят ли действия A и B в разных потоках), между действиями A и B должна существовать связь happens-before. ... Правила для happens-before: Правило порядка программы. Каждое действие потока связывается через отношение happens-before с каждым действием в том потоке, что придёт после, согласно порядку программы. Правило блокировка монитора. Разблокировка блокировки на мониторе связана отношением happens-before с каждой последующей блокировкой на той же самой блокировке монитора. Правило Volatile переменной. Запись поля volatile связана отношением happens-before со всеми последующими чтениями того же самого поля. Правило запуска потока. Вызов метода Thread.start потока связан отношением happens-before с каждым действием в запущенном потоке. Правило завершения потока. Любое действие в потоке связано отношением happens-before с любым другим потоком, определившим, что поток завершен, а также с успешным возвратом из метода Thread.join или из метода Thread.isAlive , вернувшим false. Правило прерывания. Поток, вызвавший метод interrupt другого потока, связан отношением happens-before с прерванным потоком, определившим прерывание (а также бросившим исключение InterruptedException, или вызвавшим методы isInterrupted или interrupted). Правило финализации. Завершение конструктора некоторого объекта связано отношением happens-before с запуском финализатора этого объекта. Транзитивность. Если поток A связан отношением happens-before с потоком B, и поток B связан отношением happens-before с потоком C, тогда поток A связан отношением happens-before с потоком C.
Самуил Олегович Уровень 41 Киев Украина
17 сентября 2019
А мне эти нити все мозги уже прошили вдоль и поперек.
Иван Сапронов Уровень 32 Ставрополь Россия
20 июня 2019
"Летать на самолете лучше, чем идти пешком, но сложнее." - в Гранит! :)) Лично я ещё на предыдущем уровне самостоятельно разобрал и законспектировал тему по JMM, когда нам давали задания загуглить про happens-before. Единственное, что - так и не нашёл нормального объяснения, что такое bridge-методы. Может быть кто-нибудь поделиться толковой ссылкой на объяснение этого феномена? Буду благодарен.
Даниил Уровень 41 Украина Master
12 мая 2019
Вот что я скажу просмотрев это видео и прочитав эту лекцию второй раз и прочитав комменты тут: без видео вообще В ПРИНЦИПЕ нельзя понять к чему и зачем нам это всё дают (если ты конечно не сталкивался внятно с этим вопросом). В видео хоть и не до конца для меня понятно, но раскрывают суть что же вообще такое JMM и в чём его проблемы и как решаются. Советую его посмотреть что бы понимать "зачем всё это". В кратце: happens-before - это не просто банально "что-то происходит раньше чем другое", например вызов метода Thread.start() и Thread.run() (это и так логично и понятно. Вся суть в том что happens-before обеспечивает нам то что данные будут видны в памяти всем остальным. Как бы момент синхронизации памяти...
Александр Колосов Уровень 36 Санкт-Петербург Россия
23 февраля 2019
Если вместо "happens-before" читать, "происходит(случается) до(до того, как)", то воспринимается намного проще, как само-собой разумеющееся. Так и не смог понять, в чем сложность.