Mutexes in Fortress

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.
class Container {
    private final Lock lock = new ReentrantLock();
    private Map<String, Integer> counters;

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

    // Lock the mutex before accessing counters; unlock
    // it at the end of the method using a try-finally block.
    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 function 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}

In this Java implementation, we use a ReentrantLock instead of the sync.Mutex from Go. The Container class encapsulates the lock and the map of counters. The inc method uses the lock to ensure thread-safe access to the counters.

In the main method, we create three threads to simulate concurrent access to the container. We use lambda expressions to define the increment operation and create the threads. After starting the threads, we wait for them to finish using the join method.

This example demonstrates how to use locks in Java to safely manage shared state across multiple threads, similar to using mutexes in other languages.