Mutexes in Kotlin

In the previous example, we saw how to manage simple counter state using atomic operations. For more complex state, we can use a Mutex to safely access data across multiple coroutines.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// Container holds a map of counters; since we want to
// update it concurrently from multiple coroutines, we
// add a Mutex to synchronize access.
// Note that in Kotlin, we don't need to worry about copying mutexes
// as we did in Go, because Kotlin objects are passed by reference.
class Container {
    private val mu = Mutex()
    private val counters = mutableMapOf("a" to 0, "b" to 0)

    // Lock the mutex before accessing counters; unlock
    // it at the end of the function using the withLock function.
    suspend fun inc(name: String) = mu.withLock {
        counters[name] = counters.getOrDefault(name, 0) + 1
    }

    fun getCounters(): Map<String, Int> = counters.toMap()
}

suspend fun main() = coroutineScope {
    val c = Container()

    // This function increments a named counter
    // in a loop.
    suspend fun doIncrement(name: String, n: Int) {
        repeat(n) {
            c.inc(name)
        }
    }

    // Run several coroutines concurrently; note
    // that they all access the same Container,
    // and two of them access the same counter.
    launch { doIncrement("a", 10000) }
    launch { doIncrement("a", 10000) }
    launch { doIncrement("b", 10000) }

    // Wait for the coroutines to finish
    // (This is implicitly done by coroutineScope)

    println(c.getCounters())
}

Running the program shows that the counters updated as expected.

$ kotlinc -cp kotlinx-coroutines-core-1.5.2.jar mutexes.kt -include-runtime -d mutexes.jar
$ java -jar mutexes.jar
{a=20000, b=10000}

Next, we’ll look at implementing this same state management task using only coroutines and channels.

Note: This Kotlin version uses coroutines and the kotlinx.coroutines library, which provides similar concurrency primitives to Go’s goroutines. The Mutex is used from kotlinx.coroutines.sync. You’ll need to include the kotlinx-coroutines library in your project to run this code.