Stateful Goroutines in Mercury

Here’s the translated code and explanation in Java, formatted for Hugo:

In this example, we’ll demonstrate how to use threads and synchronization in Java to manage shared state. This approach is similar to using goroutines and channels in other languages, but utilizes Java’s built-in concurrency features.

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class ReadOp {
    int key;
    BlockingQueue<Integer> resp;

    ReadOp(int key) {
        this.key = key;
        this.resp = new LinkedBlockingQueue<>();
    }
}

class WriteOp {
    int key;
    int val;
    BlockingQueue<Boolean> resp;

    WriteOp(int key, int val) {
        this.key = key;
        this.val = val;
        this.resp = new LinkedBlockingQueue<>();
    }
}

public class StatefulThreads {
    public static void main(String[] args) throws InterruptedException {
        AtomicLong readOps = new AtomicLong();
        AtomicLong writeOps = new AtomicLong();

        BlockingQueue<ReadOp> reads = new LinkedBlockingQueue<>();
        BlockingQueue<WriteOp> writes = new LinkedBlockingQueue<>();

        Thread stateManager = new Thread(() -> {
            Map<Integer, Integer> state = new HashMap<>();
            while (true) {
                try {
                    ReadOp read = reads.poll();
                    if (read != null) {
                        read.resp.put(state.getOrDefault(read.key, 0));
                        continue;
                    }

                    WriteOp write = writes.poll();
                    if (write != null) {
                        state.put(write.key, write.val);
                        write.resp.put(true);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        stateManager.start();

        for (int r = 0; r < 100; r++) {
            new Thread(() -> {
                Random rand = new Random();
                while (true) {
                    try {
                        ReadOp read = new ReadOp(rand.nextInt(5));
                        reads.put(read);
                        read.resp.take();
                        readOps.incrementAndGet();
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }).start();
        }

        for (int w = 0; w < 10; w++) {
            new Thread(() -> {
                Random rand = new Random();
                while (true) {
                    try {
                        WriteOp write = new WriteOp(rand.nextInt(5), rand.nextInt(100));
                        writes.put(write);
                        write.resp.take();
                        writeOps.incrementAndGet();
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }).start();
        }

        Thread.sleep(1000);

        System.out.println("readOps: " + readOps.get());
        System.out.println("writeOps: " + writeOps.get());
    }
}

This Java program demonstrates the use of threads to manage shared state. Here’s a breakdown of the key components:

  1. We define ReadOp and WriteOp classes to encapsulate read and write operations.

  2. The main method sets up the shared state and starts multiple reader and writer threads.

  3. A dedicated thread (stateManager) manages the shared state, processing read and write requests from other threads.

  4. We use BlockingQueues to communicate between threads, similar to channels in other languages.

  5. AtomicLong is used for thread-safe counting of operations.

  6. 100 reader threads and 10 writer threads are created, each continuously performing operations.

  7. After running for a second, the program reports the number of read and write operations performed.

To run the program, compile and execute the Java file:

$ javac StatefulThreads.java
$ java StatefulThreads
readOps: 71708
writeOps: 7177

This thread-based approach in Java is somewhat more complex than using simple locks, but it can be beneficial in scenarios involving multiple synchronization points or when dealing with more complex communication patterns between threads. Choose the approach that makes your program’s correctness easiest to reason about and maintain.