Dining Philosophers with Ordered Forks

Problem

n philosophers sit around a table, with one fork between each two neighbours (n forks in a circle). Philosopher i needs fork i and fork (i + 1) mod n to eat. Each philosopher eats m times. Make every philosopher a thread that takes the forks, eats, and puts the forks down. The program must never deadlock. Print the total number of meals eaten.

Constraints

  • 2 <= n <= 8
  • 1 <= m <= 1000

Examples

Input

5 3

Output

15

Solutions

Best approachTime O(n m)Space O(n)

If everybody takes the left fork first, all can hold one fork and wait for the other: a deadlock. When every thread takes forks in the same global order (smaller number first), a circle of waiting is impossible. At least one philosopher can always eat.

Same code works in Java810172125Basic features only, so nothing changes between versions.

JavaScript and TypeScript have no shared-memory threads here, so they follow the same steps in one thread.

/*
 * Problem : Count the meals.
 * Input   : n and m.
 * Output  : The total number of meals.
 * Approach: Ordered forks
 * Time    : O(n m)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
    // This method solves the task. It receives the input and returns the answer.
    static long solve(long a, long b) {
        int n = (int) a;
        int meals = (int) b;
        Object[] forks = new Object[n];
        for (int i = 0; i < n; i++) {
            forks[i] = new Object();
        }
        java.util.concurrent.atomic.AtomicLong eaten = new java.util.concurrent.atomic.AtomicLong();
        Thread[] threads = new Thread[n];
        for (int i = 0; i < n; i++) {
            // Step 1: Every philosopher takes the fork with the smaller number first. This global order cannot make a circle of waiting.
            final Object first = forks[Math.min(i, (i + 1) % n)];
            final Object second = forks[Math.max(i, (i + 1) % n)];
            threads[i] = new Thread(() -> {
                for (int k = 0; k < meals; k++) {
                    // Step 2: Hold the first fork, then take the second one, then eat.
                    synchronized (first) {
                        synchronized (second) {
                            eaten.incrementAndGet();
                        }
                    }
                }
            });
            threads[i].start();
        }
        // Step 3: Wait for every philosopher to finish and read the total.
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return eaten.get();
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long a = scanner.nextLong();
        long b = scanner.nextLong();
        System.out.println(solve(a, b));
    }
}
Hints (2)
  1. Which four conditions must hold for a deadlock?
  2. Which one does the order break?