import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class Semaphores {
    public static void main(String[] args) throws Exception {
        if (args.length > 0) FAIR = true;
        PERMITS = PHILS; // - 1;
        System.err.format("*** PHILS=%d, PERMITS=%d, STEPS=%d, FAIR=%b%n%n", PHILS, PERMITS, STEPS, FAIR);
        
        Semaphore sem = new Semaphore(PERMITS, FAIR);        
        ArrayList<Thread> threads = new ArrayList<Thread>();
        for (int i=1; i<=PHILS; i++) threads.add(new Philosopher(sem, i));
        
        for (Thread t : threads) t.start();
        /* *** */
        for (Thread t : threads) t.join();
        
        System.err.println("\r\n<enter> to exit");
        System.in.read();
    }
    
    static int PHILS = 3;
    static int PERMITS = 2;
    static int STEPS = 3;
    static boolean FAIR = false;
    static int THINK_SLEEP = 3000;
    static int EAT_SLEEP = 3000;

    static class Philosopher extends Thread {
        private Semaphore sem;
        private int id;
            
        public Philosopher(Semaphore sem, int id) {
            this.sem = sem;
            this.id = id;
            this.setName("W"+id);
        }

        public void run() {
            System.err.format("[%d] P%d starts%n", sem.availablePermits(), id);
            
            for (int step = 0; step < STEPS; step+=1) {
                System.err.format("[%d] \tP%d acquire?%n", sem.availablePermits(), id);
                try { sem.acquire(); } catch (InterruptedException ex) {}
                System.err.format("[%d] \tP%d acquire!%n", sem.availablePermits(), id);

                System.err.format("[%d] P%d eats...%n", sem.availablePermits(), id);
                try { Thread.sleep((int)(Math.random() * EAT_SLEEP)); } catch (InterruptedException ex) {}
                
                System.err.format("[%d] \tP%d release?%n", sem.availablePermits(), id);
                sem.release();
                System.err.format("[%d] \tP%d release!%n", sem.availablePermits(), id);
                System.err.format("[%d] P%d thinks...%n", sem.availablePermits(), id);
                try { Thread.sleep((int)(Math.random() * THINK_SLEEP)); } catch (InterruptedException ex) {}
            }
            
            System.err.format("[%d] P%d ends%n", sem.availablePermits(), id);
        }
    }
}



