Ответ
volatile — это ключевое слово в Java (и аннотация @Volatile в Kotlin), которое гарантирует видимость изменений переменной для всех потоков. Оно указывает, что переменная может быть изменена одновременно несколькими потоками, и ее значение не должно кэшироваться в регистрах или локальной памяти потока.
Решаемая проблема: Без volatile один поток может изменить переменную в своей локальной кэш-памяти, а другой поток продолжит читать устаревшее значение из основной памяти или своего кэша.
Пример (Kotlin):
class WorkerTask {
@Volatile // Аннотация в Kotlin
var isRunning = true
fun startBackgroundWork() {
thread(name = "WorkerThread") {
// Фоновый поток читает isRunning из основной памяти
while (isRunning) {
// Выполняем полезную работу
Thread.sleep(100)
}
println("Фоновый поток завершился.")
}
}
fun stopWork() {
// Главный поток записывает новое значение в основную память
isRunning = false
println("Флаг остановки установлен.")
}
}
// Использование
val task = WorkerTask()
task.startBackgroundWork()
Thread.sleep(1000)
task.stopWork() // Без @Volatile фоновый поток может не увидеть изменение isRunning
Что гарантирует volatile:
- Видимость (Visibility): Запись в
volatile-переменную происходит напрямую в основную память, а чтение — из основной памяти. Это делает изменение немедленно видимым для всех других потоков. - Запрет переупорядочивания (Ordering): Операции чтения/записи
volatile-переменной не могут быть переупорядочены компилятором или процессором относительно других операций с памятью, что создает частичный барьер памяти.
Чего НЕ гарантирует volatile:
- Атомарность (Atomicity):
volatileне делает составные операции (например, инкрементcounter++) атомарными. Такая операция состоит из чтения, изменения и записи, и другой поток может вмешаться между этими шагами.
Когда использовать:
- Для простых флагов состояния (
isRunning,isInitialized). - Когда только один поток пишет, а многие читают.
Альтернативы для сложных случаев:
- Атомарные операции: Используйте классы из
java.util.concurrent.atomic(например,AtomicInteger). - Синхронизация: Используйте
synchronizedблоки илиLock. - Потокобезопасные коллекции:
ConcurrentHashMapи другие. - Корутины Kotlin: Используйте
Mutexили потокобезопасные примитивы изkotlinx.coroutines.