Mutexes in Groovy

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 threads.

import java.util.concurrent.locks.ReentrantLock

class Container {
    private final ReentrantLock lock = new ReentrantLock()
    private Map<String, Integer> counters = [:]

    // Lock the mutex before accessing `counters`; unlock
    // it at the end of the method using a try-finally block.
    void inc(String name) {
        lock.lock()
        try {
            counters[name] = (counters[name] ?: 0) + 1
        } finally {
            lock.unlock()
        }
    }
}

// Note that the ReentrantLock is initialized in the constructor,
// so no additional initialization is required here.
def c = new Container(counters: ["a": 0, "b": 0])

// This closure increments a named counter in a loop.
def doIncrement = { String name, int n ->
    n.times { c.inc(name) }
}

// Run several threads concurrently; note
// that they all access the same Container,
// and two of them access the same counter.
def threads = [
    Thread.start { doIncrement("a", 10000) },
    Thread.start { doIncrement("a", 10000) },
    Thread.start { doIncrement("b", 10000) }
]

// Wait for the threads to finish
threads*.join()

println c.counters

Running the program shows that the counters updated as expected.

$ groovy mutexes.groovy
[a:20000, b:10000]

Next we’ll look at implementing this same state management task using only threads and message passing.

In this Groovy example:

  1. We use ReentrantLock instead of sync.Mutex for thread synchronization.
  2. The Container class encapsulates the lock and the counters.
  3. We use a closure doIncrement instead of a function for the incrementing logic.
  4. Groovy’s Thread.start is used to create and start threads, replacing Go’s goroutines.
  5. We use threads*.join() to wait for all threads to complete, which is equivalent to WaitGroup in Go.
  6. The map syntax and string interpolation in Groovy make the code more concise in some places.

This translation maintains the core concepts of the original Go code while adapting to Groovy’s syntax and concurrency model.