//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// author: qingzhe huang
// email: qingz_hu@cse.concordia.ca
// date: oct. 1, 2006
// purpose: 	This little example tries to show you how context switch may corrupt your data when your
// 		program is not properly synchronized with global data access
//	1. since java 1.2.1 doesn't have class CyclicBarrier, I have to make it myself to barrier all
//	threads at beginning so that roughly all threads start running at same time.
//	2. I create two group of equal number threads. One group try to increase the global variable "myPrice"
//	The other group try to decrease it. Since their number are equal, the balance should remain unchanged if
//	everything goes well.
//	3. In order to make context switch happen in java 1.2.1, you must have a "sleep" or "yield" in function
//	"incPrice" and/or "decPrice". In java 1.4 or up, even you don't have this deliberate "waiting", your
//	balance still is not unchanged which means data corrupted because the threading-mode is "preemptive".
//	4. In java 1.2.1, you can play with the "sleep" in "incPrice" or "decPrice" by setting up different time to
//	sleep, say 0 or 200 etc. and observe what is the result. Do they have patterns? Or remain fixed with one 
//	setting? Or you are in the mood for adventure, try random number with Rand.nextInt(100); Or modify the 
//	"MaxThreadNumber" to see what may change or not?
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

import java.util.*;

public class MyExample
{
	public static final int MaxThreadNumber=30;
	//////////////////////////////////////
	//here we declare two equal number of array of threads and initialize them
	public static IncThread[] incThreadArray=new IncThread[MaxThreadNumber];
	public static DecThread[] decThreadArray=new DecThread[MaxThreadNumber];

	//////////////////////////////////
	//a small skill to stop all created threads run one by one is to use 
	//a barrier to "stop them until all threads are ready
	//as java 1.2 doesn't have this "CyclicBarrier class, I have to make it myself
	////////////////////////////////////////////////
	public static int threadCounter=0;
	
	public static Random rand=new Random();
	//here it is the one we are watching...
	public static int myPrice;
  
	public static void main(String args[]) 
	{
		try 
		{
			myPrice=5000;
			System.out.println("before thread testing starts, let's see the balance of myPrice: "+myPrice);

			System.out.println("Since we are using equal number of threads to run increasing and decreasing, if"); 
			System.out.println(" our system is properly synchronized, after testing the balance should remain unchanged.");

			////////////////////////////////////
			//since we have a "barrier" setup, the threads won't start until all are ready
			for (int i=0; i<MaxThreadNumber; i++)
			{
				incThreadArray[i]=new IncThread();
				incThreadArray[i].start();
				decThreadArray[i]=new DecThread();
				decThreadArray[i].start();
			}

			////////////////////////////////////
			//Here our primary thread or main thread must wait all threads to finish
			for (int i=0; i<MaxThreadNumber; i++)
			{			
				incThreadArray[i].join();		
				decThreadArray[i].join();
			}
			//then we print our result.
			System.out.println("Now the testing ends, let's check if the balance is unchanged: "+ myPrice);
 
		} // end try 
		catch (Exception e) 
		{
			System.out.println("Exception in HelloClient: " + e);
		} 
	}


		///////////////////////////////////////////////////
	//here we define two helper class which is thread and they do increasing, decreasing price.
	/////////////////////////////////////////////////
	public static class IncThread extends Thread
	{		
		//every time we call this function we increase the "myPrice" by 100
		public void incPrice()
		{
			//deliberately I want to make this method NOT THREAD-SAFE to demonstrate my point
			int temp;
			//this is the place where context switch might happen and our price might be corrupted
			temp= myPrice - 100;
		
			//here let's deliberately put thread in sleeping for a while
			//so that we can create chances for other thread to get context switch
			try
			{
				//Thread.sleep(50); //---> If you comment out this statement, the thread will run through in java 1.2.1
					           //---> then nothing seems happened and you won't be able to observe the different 
						   //---> balance problem due to context switch
				Thread.sleep(rand.nextInt(500));
			}
			catch(Exception er)
			{
				System.out.println(er);
			}
			myPrice=temp;
		}


		public void run()
		{
			try
			{
				threadCounter++;
				while (threadCounter<MaxThreadNumber*2)
				{
					//let's wait for other thread ready
					Thread.sleep(10);
				}
				//now everybody is ready
				incPrice();
			}
			catch(Exception er)
			{
				System.out.println(er);
			}
		}
	}

	public static class DecThread extends Thread
	{
		//every time we call this function we increase the "myPrice" by 100
		public  void decPrice() 
		{
			//deliberately I want to make this method NOT THREAD-SAFE to demonstrate my point
			int temp;
			//this is the place where context switch might happen and our price might be corrupted
			temp= myPrice + 100;
		
			//here let's deliberately put thread in sleeping for a while
			//so that we can create chances for other thread to get context switch
			try
			{
				//Thread.sleep(100); //---> If you comment out this statement, the thread will run through in java 1.2.1
					           //---> then nothing seems happened and you won't be able to observe the different 
						   //---> balance problem due to context switch
				Thread.sleep(rand.nextInt(500));
			}
			catch(Exception er)
			{
				System.out.println(er);
			}
			myPrice=temp;
		}

		public void run()
		{
			try
			{
				threadCounter++;
				while (threadCounter<MaxThreadNumber*2)
				{
					//let's wait for other thread ready
					Thread.sleep(10);
				}
				//now everybody is ready
				decPrice();
			}
			catch(Exception er)
			{
				System.out.println(er);
			}
		}
	}
} // end class
