import java.util.*;

/**
 * Silly demonstration of thread troubles. Putters add, Takers take.
 * In first version, nothing is synchronized, we'll change this.
 * to fix things -- (java ... 5 3 should cause problems)
 */

public class ThreadTrouble
{
    private LinkedList myList = new LinkedList();
    private Random     myRando = new Random();
    
    public ThreadTrouble(int putCount, int takeCount) {
	for(int k=0; k < putCount; k++){
	    Putter put = new Putter(this);
	    put.start();
	}
	for(int k=0; k < takeCount; k++){
	    Taker take = new Taker(this);
	    take.start();
	}
    }

    // Add an object to this collection
    
    public void put(Putter p, Object o) {
	System.out.println(p+" added "+o);
	myList.add(o);
    }

    /**
     * Take a random number of object out (if there are
     * any) and return the last one take. Returns null
     * if none to take. The loop can cause synchronization
     * issues, e.g., if two takers both try to remove 5 from
     * a list of 6 elements.
     */
    public  Object take(Taker t) {
	if (myList.size() > 0){
	    try{
		int index = myRando.nextInt(myList.size());
		System.out.println(t + " tries to take "+index);

		synchronized (this){
		    for(int k=0; k < index-1; k++){
			myList.removeLast();
		    }
		    return myList.removeLast();		    
		}

	    }
	    catch (Exception e){
		t.interrupt();
	    }

	}
	return null;
    }

    public static void main(String[] args) {
	int putCount = 1;
	int takeCount = 1;
	if (args.length >= 1) {
	    putCount = Integer.parseInt(args[0]);
	}
	if (args.length == 2) {
	    takeCount = Integer.parseInt(args[1]);
	}
	
	ThreadTrouble ttrouble = new ThreadTrouble(putCount,takeCount);
    }
}

/**
 * A threaded putter that adds elements, sleeps,
 * and then adds some more.
 */
class Putter extends Thread
{
    ThreadTrouble mySource;
    
    public Putter(ThreadTrouble t) {
	mySource = t;
	myPutCount = ourTotalNumberOfPutters++;
    }
    
    public void run() {
	while (true){
	    mySource.put(this,new Integer(ourCount++));
	    try{
		Thread.sleep(500); 
	    }
	    catch (InterruptedException e) {
		System.out.println("putter stopped");
		break;
	    }	    
	}	
    }

    public String toString()
    {
	return ""+myPutCount;
    }
    private int myPutCount;
    private static int ourCount = 0;
    private static int ourTotalNumberOfPutters = 0;
}

/**
 * A threaded taker that tries to take elements, sleeps,
 * then tries to take some more.
 */

class Taker extends Thread
{
    private ThreadTrouble mySource;
    private int myCount;
    private static int ourCount;
    
    public Taker(ThreadTrouble t) {
	mySource = t;
	myCount = ourCount++;
    }
    
    public void run() {
	while (true){
	    Object o = mySource.take(this);
	    if (o != null){
		System.out.println("\t took "+o);
	    }
	    
	    try{
		Thread.sleep(500); 
	    }
	    catch (InterruptedException e) {
		System.out.println("\t\t\ttaker " + myCount+ " stopped");
		ourCount--;
		if (ourCount == 1){
		    System.out.println("only one taker left, stopping");
		    System.exit(0);
		}
		break;
	    }	    
	}
    }

    public String toString() {
	return ""+myCount;
    }
}
