Mutexes in Mercury

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.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// Container holds a map of counters; since we want to
// update it concurrently from multiple threads, we
// add a Lock to synchronize access.
// Note that locks must not be copied, so if this
// class is passed around, it should be done by
// reference.
class Container {
    private final Lock lock = new ReentrantLock();
    private final Map<String, Integer> counters;

    public Container() {
        counters = new HashMap<>();
        counters.put("a", 0);
        counters.put("b", 0);
    }

    // Lock the mutex before accessing counters; unlock
    // it at the end of the method.
    public void inc(String name) {
        lock.lock();
        try {
            counters.put(name, counters.get(name) + 1);
        } finally {
            lock.unlock();
        }
    }

    public Map<String, Integer> getCounters() {
        return new HashMap<>(counters);
    }
}

public class Mutexes {
    public static void main(String[] args) throws InterruptedException {
        Container c = new Container();

        // This method increments a named counter
        // in a loop.
        Runnable doIncrement = (String name, int n) -> {
            for (int i = 0; i < n; i++) {
                c.inc(name);
            }
        };

        // Run several threads concurrently; note
        // that they all access the same Container,
        // and two of them access the same counter.
        Thread t1 = new Thread(() -> doIncrement.run("a", 10000));
        Thread t2 = new Thread(() -> doIncrement.run("a", 10000));
        Thread t3 = new Thread(() -> doIncrement.run("b", 10000));

        t1.start();
        t2.start();
        t3.start();

        // Wait for the threads to finish
        t1.join();
        t2.join();
        t3.join();

        System.out.println(c.getCounters());
    }
}

Running the program shows that the counters updated as expected.

$ javac Mutexes.java
$ java Mutexes
{a=20000, b=10000}

Next we’ll look at implementing this same state management task using only threads and concurrent data structures.