import java.util.concurrent.*;
import java.util.Scanner;

public class OutOfSync {

	public static final int WORKERS = 10;
	
	
	public static void main(String[] args) {
		DataCollector shared = new DataCollector();
		
		ExecutorService threadExecutor = Executors.newCachedThreadPool();
		
		SumWorker[] workers = new SumWorker[WORKERS];
		for(int i = 0; i < WORKERS; i++){
			//create, store and start workers
			workers[i] = new SumWorker(shared);
			threadExecutor.execute(workers[i]);
		}
		System.out.println("Done creating.");
		
		//wait for the user to type something
		Scanner in = new Scanner(System.in);
		System.out.println("Type anything to end: ");
		in.nextLine();
		
		//tell the workers to finish
		for(SumWorker w: workers){
			w.finish();
		}
		
		//returns immediately, but takes no new workers
		threadExecutor.shutdown();
		
		//wait for all the threads to complete
		try {
			threadExecutor.awaitTermination(5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//output results
		//sum up workers' totals
		int sums = 0;
		for(SumWorker w: workers){
			sums += w.getMyTotal();
		}
		
		System.out.printf("Sum: %d =?= collector: %d =?= shared: %d\n",
				sums, shared.getTotal(), SumWorker.sharedSum);
		
//		System.out.println("Type anything to end: ");
//		in.nextLine();
//
//		System.out.printf("Sum: %d =?= collector: %d =?= shared: %d\n",
//				sums, shared.getTotal(), SumWorker.sharedSum);
	}

}

//worker  class
class SumWorker implements Runnable{

	public static long sharedSum = 0;
	
	
	private DataCollector data;
	private long myTotal;

	//don't use a cached copy of this variable
	//check the value in memory
	//option 1: make the variable volatile
	private volatile boolean done;

	public SumWorker(DataCollector dc){
		data = dc;
		myTotal = 0;
		done = false;
	}

	@Override
	public void run(){
		while(!done){
			myTotal++;
			data.increment();
			
			//Problem 2: shared resource: threads are overwriting
			//make the three-step increment happen as if it is one step
			//sharedSum++;
			incrementShared();
			//let something else run
			//option 2: switch to ready state
			//Thread.yield();
		}
	}

	public void finish(){
		done = true;
	}

	public long getMyTotal(){
		return myTotal;
	}
	
	//only a single thread access this method at a time
	//non-static: locks the object (SumWorker)
	//static: locks the class
	public static synchronized void incrementShared() {
		sharedSum++;
	}
	
}


//a class to hold the shared data
class DataCollector {
	private long total;
	
	public DataCollector(){
		total = 0;
	}
	
	public synchronized void increment(){
		total++;
	}
	
	public synchronized void decrement(){
		total--;
	}
	
	public long getTotal(){
		return total;
	}
}