// Loosely based on Arnold, Gosling, Holmes p. 252

class PhilosopherThread implements Runnable {
    /*@non_null@*/ private Philosopher philosopher;

    public PhilosopherThread (/*@non_null@*/ Philosopher p) {
	philosopher = p;
    }

    public void run () {
	philosopher.philosophize ();
    }
}

public class Philosopher {
    private Philosopher colleague;
    private String name;
    private String quote;

    public Philosopher (String name, String quote) {
	this.name = name;
	this.quote = quote;
    }

    public synchronized void setColleague (Philosopher p) {
	colleague = p;
    }

    public void philosophize () {
	Object lock1, lock2;

	if (colleague != null) { // Need a colleague to start and argument.
	    // Always grab the lock for whichever name is alphabetically first
	    if (name.compareTo (colleague.name) < 0) {
		lock1 = this;
		lock2 = colleague;
	    } else {
		lock1 = colleague;
		lock2 = this;
	    }
	        
	    synchronized (lock1) {
		synchronized (lock2) {
		    System.err.println (name + "[Thread " + Thread.currentThread().getName () + "] says " + quote);
		    colleague.argue ();
		} 
	    }
	}
    }

    public synchronized void argue () {
	System.err.println (name + "[Thread " + Thread.currentThread().getName () + "] argues: No! " + quote);
    }

    public static void main (String [] args) {
	Philosopher descartes = new Philosopher ("Descartes", "I think, therefore I am.");
	Philosopher plato = new Philosopher ("Plato", "The life which is unexamined is not worth living.");
	
	descartes.setColleague (plato);
	plato.setColleague (descartes);

	Thread dthread = new Thread (new PhilosopherThread (descartes));
	Thread pthread = new Thread (new PhilosopherThread (plato));

	dthread.start ();
	pthread.start ();
    }
}	
