kotlinadvanced

Coroutine Mutex and Synchronization

Synchronize shared mutable state in coroutines with Mutex, atomic operations, and thread confinement.

kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.atomic.AtomicInteger

fun main() = runBlocking {
    // Problem: shared mutable state without sync
    var unsafeCounter = 0
    coroutineScope {
        repeat(1000) {
            launch(Dispatchers.Default) { repeat(100) { unsafeCounter++ } }
        }
    }
    println("Unsafe: $unsafeCounter") // likely < 100000

    // Solution 1: Mutex
    var mutexCounter = 0
    val mutex = Mutex()
    coroutineScope {
        repeat(1000) {
            launch(Dispatchers.Default) {
                repeat(100) { mutex.withLock { mutexCounter++ } }
            }
        }
    }
    println("Mutex: $mutexCounter") // exactly 100000

    // Solution 2: AtomicInteger
    val atomicCounter = AtomicInteger(0)
    coroutineScope {
        repeat(1000) {
            launch(Dispatchers.Default) {
                repeat(100) { atomicCounter.incrementAndGet() }
            }
        }
    }
    println("Atomic: ${atomicCounter.get()}") // exactly 100000

    // Solution 3: Thread confinement
    val singleThread = newSingleThreadContext("counter")
    var confinedCounter = 0
    coroutineScope {
        repeat(1000) {
            launch(Dispatchers.Default) {
                withContext(singleThread) { repeat(100) { confinedCounter++ } }
            }
        }
    }
    println("Confined: $confinedCounter")
    singleThread.close()

    // Mutex for protecting shared resource
    class BankAccount {
        private var balance = 0.0
        private val mutex = Mutex()
        suspend fun deposit(amount: Double) = mutex.withLock {
            delay(1); balance += amount
        }
        suspend fun getBalance() = mutex.withLock { balance }
    }

    val account = BankAccount()
    coroutineScope {
        repeat(100) { launch { account.deposit(10.0) } }
    }
    println("Balance: ${account.getBalance()}") // exactly 1000.0
}

Use Cases

  • Thread-safe shared state in coroutines
  • Concurrent counter and accumulator patterns
  • Protecting critical sections in async code

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.