← Назад к вопросам
Как работает механизм синхронизации?
2.2 Middle🔥 191 комментариев
#JVM и память#Многопоточность и асинхронность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизм синхронизации в Kotlin
Определение
Синхронизация — это механизм обеспечения потокобезопасного доступа к общим ресурсам, когда несколько потоков пытаются получить доступ одновременно.
Проблема Race Condition
Без синхронизации:
var counter = 0
fun increment() {
counter++ // ❌ НЕБЕЗОПАСНО!
}
// Если 2 потока вызовут increment() одновременно:
// Thread 1: читает counter (0) → увеличивает → пишет 1
// Thread 2: читает counter (0) → увеличивает → пишет 1
// Результат: counter = 1 вместо 2 (потеря данных!)
1. Synchronized блок
var counter = 0
val lock = Object()
fun increment() {
synchronized(lock) {
counter++ // ✅ Потокобезопасно
}
}
// Thread 1 ждёт, пока Thread 2 закончит
// Результат: counter = 2 (правильно)
Как это работает:
- Поток пытается войти в synchronized блок
- Если блок занят другим потоком, ждёт в очереди
- Когда блок освобождается, следующий поток входит
- Это гарантирует, что только один поток выполняет код одновременно
Визуализация:
Thread 1: ┌─────────────────────┐
│ synchronized(lock) │
│ counter++ │ → counter = 1
└─────────────────────┘
↓
Блок свободен
↓
Thread 2: ┌─────────────────────┐
│ synchronized(lock) │
│ counter++ │ → counter = 2
└─────────────────────┘
2. Synchronized функция
class Counter {
private var value = 0
@Synchronized
fun increment() {
value++ // Синхронизирована на this
}
@Synchronized
fun getValue(): Int = value
}
// Эквивалентно:
class Counter {
private var value = 0
fun increment() {
synchronized(this) {
value++
}
}
}
3. ReentrantLock
import java.util.concurrent.locks.ReentrantLock
class Counter {
private var value = 0
private val lock = ReentrantLock()
fun increment() {
lock.lock()
try {
value++
} finally {
lock.unlock() // ВСЕГДА отпустить!
}
}
// Или короче:
fun decrement() {
lock.withLock {
value--
}
}
}
ReentrantLock vs synchronized:
- Можно попробовать получить lock:
lock.tryLock() - Можно установить timeout:
lock.tryLock(5, TimeUnit.SECONDS) - Более гибкий (например, fair queue)
4. Volatile
volatile var isRunning = false
// Гарантирует:
// - Видимость изменений между потоками
// - НО не гарантирует атомарность операций
fun stop() {
isRunning = false // ✅ Все потоки видят это изменение
}
// НЕПРАВИЛЬНО:
volatile var counter = 0
counter++ // ❌ Всё равно race condition!
// (читай + пиши = 2 операции, не атомарная)
5. AtomicInteger / AtomicReference
import java.util.concurrent.atomic.AtomicInteger
val counter = AtomicInteger(0)
fun increment() {
counter.incrementAndGet() // ✅ Атомарная операция
}
fun getValue(): Int = counter.get()
// Другие операции:
counter.addAndGet(5)
counter.compareAndSet(0, 1) // CAS (Compare-And-Swap)
counter.decrementAndGet()
CAS (Compare-And-Swap) механизм:
// "Если значение 0, то установи 1"
val success = counter.compareAndSet(0, 1)
if (success) {
println("Успешно установили 1")
} else {
println("Значение изменилось, повторим...")
}
6. Mutex в Coroutines
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
val mutex = Mutex()
var counter = 0
suspend fun increment() {
mutex.withLock {
counter++
}
}
// Или вручную:
suspend fun decrement() {
mutex.lock()
try {
counter--
} finally {
mutex.unlock()
}
}
7. Semaphore
import java.util.concurrent.Semaphore
val semaphore = Semaphore(3) // Максимум 3 потока одновременно
fun processRequest() {
semaphore.acquire() // Ждёт, если уже 3 потока
try {
// Обработка (максимум 3 одновременно)
println("Processing...")
} finally {
semaphore.release()
}
}
Сравнительная таблица
| Способ | Потокобезопасность | Производительность | Использование |
|---|---|---|---|
| synchronized | ✅ Да | ❌ Низкая | Простые случаи |
| ReentrantLock | ✅ Да | ✅ Выше | Нужна гибкость |
| volatile | ⚠️ Только видимость | ✅✅ Высокая | Флаги, счётчики |
| AtomicInteger | ✅ Да (CAS) | ✅✅ Высокая | Счётчики |
| Mutex | ✅ Да | ✅ Для coroutines | Корутины |
Пример: Потокобезопасный счётчик
class ThreadSafeCounter {
// ❌ НЕПРАВИЛЬНО
private var count = 0
fun increment() { count++ }
// ✅ ПРАВИЛЬНО 1
private var count1 = 0
synchronized fun increment1() { count1++ }
// ✅ ПРАВИЛЬНО 2
private val count2 = AtomicInteger(0)
fun increment2() { count2.incrementAndGet() }
// ✅ ПРАВИЛЬНО 3
private var count3 = 0
private val lock = ReentrantLock()
fun increment3() {
lock.withLock { count3++ }
}
}
Deadlock: опасность синхронизации
// ❌ DEADLOCK
val lock1 = Object()
val lock2 = Object()
fun process1() {
synchronized(lock1) {
Thread.sleep(100) // Имитация работы
synchronized(lock2) { // Ждёт lock2
println("Process 1")
}
}
}
fun process2() {
synchronized(lock2) {
Thread.sleep(100)
synchronized(lock1) { // Ждёт lock1
println("Process 2")
}
}
}
// Если запустить параллельно: DEADLOCK!
Правило: Избегать Deep Nesting
// ❌ Сложно (deadlock risk)
synchronized(lock1) {
synchronized(lock2) {
// код
}
}
// ✅ Лучше
val combinedLock = Object()
synchronized(combinedLock) {
// код, защищающий оба ресурса
}
Итог
Механизм синхронизации обеспечивает потокобезопасность за счёт:
- Блокировки доступа (synchronized, Lock)
- Атомарных операций (AtomicInteger, CAS)
- Видимости между потоками (volatile)
Для новых проектов: используй Coroutines + Mutex вместо потоков и synchronized.