Но что если нам нужно прочитать какое-то значение, пройтись в when операторе, а потом записать новое, можно конечно попробовать сделать это через @Volatile:
@Volatile var _state: Int
when(_state) {
NONE -> _state = PENDING
...
}
Но проблема в том, что может произойти ситуация когда поток X прочитает актуальное значение, а в этот момент поток Y запишет другое, что приведет к нелогичным данным.
Глянем решение с compareAndSet(), пример взят из исходников StateFlow:
val _state = atomic(0)
while(true) {
val state = _state.value
when(state) {
NONE -> if (_state.compareAndSet(state, PENDING)) break
...
}
}
Метод
compareAndSet() под капотом реализован процессорной инструкцией, эта инструкция выполняется атомарно и следовательно не нуждается в синхронизации между потоками, работает она следующим образом:1) читает актуальное значение переменной
_state из памяти как это делает @Volatile 2) сравнивает актуальное значение с текущим
state, которое как мы уже знаем может быть неактуальным3) если значение актуально, записывает новое
PENDING и возвращает true4) если значение неактуально возвращает false
Когда поток Y запишет новое значение, поток X начнёт выполнять метод
compareAndSet(), который вернёт false, так как данные неактуальны, далее начнётся новая итерация цикла, это будет продолжаться до тех пор пока на потоке X не появятся актуальные данные и только когда это произойдёт запишутся новые.P.S. Использование бесконечного цикла с инструкцией compareAndSet() это нормальная практика, применяется много где: начиная с корутин и заканчивая Android SDK.
Пишите ваше мнение в комментах и всем хорошего кода!
