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
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.
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)
- Which four conditions must hold for a deadlock?
- Which one does the order break?