Начнем с того, что это не проблема многопоточности, а скорее некоторая абстракция, или даже набор правил. Для наглядности начнем с кода. Представим, что у нас есть две функции
operationFirst() и operationSecond(), которые что-то делают, как-то изменяют состояние объекта:
class Some {
private var x = 0
private var y = 0
fun operationFirst(){
x++
}
fun operationSecond(){
y++
}
}
Далее у нас есть два потока, поток first и поток second, функция
operationFirst() вызывается в потоке first, функция operationSecond() вызывается в потоке Second.
val some = Some()
val first = thread { some.operationFirst() }
val second = thread { some.operationSecond() }
Теперь следим за руками, если сказано, что
operationFirst() happens-before operationSecond() это означает что все изменения, которые сделал поток first до момента вызова функции operationFirst() и изменения, которые произошли в самой функции operationFirst() будут видны потоку second в момент вызова функции operationSecond(). Помните мы разбирали, проблему с Visibility, так вот если сказано, что гарантируется
operationFirst() happens-before operationSecond(), то это значит, что проблемой с Visibility точно не будет, поток second точно увидит актуальное значение переменных. Мы также затрагивали проблему Reordering? Если гарантируется happens-before, то переупорядочивание нам тоже не страшно.Как использовать это на практике? Самый простой способ просто использовать синхронизацию:
val lock = Lock()
val first = thread { lock.withLock { some.operationFirst() } }
val second = thread { lock.withLock { some.operationSecond() } }
first.join()
second.join()
Представим, что поток first точно запуститься первым. В примере получается, что
operationFirst() happens-before operationSecond(), следовательно, все что будет сделано в потоке first, увидит поток second в момент исполнения функции operationSecond().Для примера, несколько вещей в java в которых гарантируется happens-before:
👉Правило запуска потока. Вызов Thread.start на потоки происходит перед каждым действием в запущенном потоке.
👉Правило мониторного замка. Операция unlock на мониторном замке происходит перед каждой последующей операцией lock на том же самом мониторном замке.
👉Правило финализатора. Завершение конструктора объекта происходит перед началом финализатора этого объекта.
🧐Немного запутанная штука, но по сути достаточно понять что показано в картинке и уметь это как-то объяснить.