public class ReaderWriter2
{
	/**
 	* argv[0]: number of concurrently executed readers<br/>
 	* argv[1]: number of concurrently executed writers<br/>
 	* argv[2]: number of reads per executed reader<br/>
 	* argv[3]: number of reads per executed writer<br/>
	*/
	public static void main(String argv[])
{
		IntCRW data = new IntCRW();
		for (int i=0; i<Integer.parseInt(argv[0]); i++)
		{
			new Thread(new Writer(data, Integer.parseInt(argv[2]))).start();
		} //for
		for (int i=0; i<Integer.parseInt(argv[1]); i++)
		{
			new Thread(new Reader(data, Integer.parseInt(argv[3]))).start();
		} //for
	} //main()
} //class ReaderWriter2
//---------------------------------------------------------
class IntCRW extends AccessControl
{
	int data;
	
	protected Object reallyRead()
	{
		return new Integer(data);
	} //reallyRead()
	
	protected void reallyWrite(Object obj)
	{
		data = ((Integer) obj).intValue();
	} //reallyWrite()
} //class IntCRW
//---------------------------------------------------------
class Reader implements Runnable
{
	private IntCRW data;
	private int noOfReads;

	public Reader(IntCRW data, int noOfReads)
	{
		this.data = data;
		this.noOfReads = noOfReads;
	} //constructor
	
	public void run()
	{
		for (int i=0; i<noOfReads; i++)
		{
			Integer myInt = (Integer) data.read();
			System.out.println(Thread.currentThread().getName()+" read value "+myInt );
		} //for
	} //run()
} //class Reader()
//---------------------------------------------------------
class Writer implements Runnable
{
	private IntCRW data;
	private int noOfWrites;

	public Writer(IntCRW data, int noOfWrites)
	{
		this.data = data;
		this.noOfWrites = noOfWrites;
	} //constructor
	
	public void run()
	{
		int value;
		for (int i=0; i<noOfWrites; i++)
		{
			value = (int) (Math.random()*Integer.MAX_VALUE);
			data.write(new Integer( value ));
			System.out.println(Thread.currentThread().getName()+" wrote value "+value );
		} //for
	} //run()
} //class Writer			
//---------------------------------------------------------
abstract class AccessControl
{
	private Semaphore2 sem = new Semaphore2(100);
		
	protected abstract Object reallyRead();
	protected abstract void reallyWrite(Object obj);
	
	public Object read()
	{
		beforeRead();
		Object obj = reallyRead();
		afterRead();
		
		return obj;
	} //read()
	
	public void write(Object obj)
	{
		beforeWrite();
		reallyWrite(obj);
		afterWrite();
	} //write()
	
	private synchronized void beforeRead()
	{
		sem.p();
	} //beforeRead()
	
	private synchronized void afterRead()
	{
		sem.v();
	} //afterRead
	
	private synchronized void beforeWrite()
	{
		sem.pAll();
	} //beforeWrite()
	
	private synchronized void afterWrite()
	{
		sem.vAll();
	} //afterWrite()
} //class AccessControl
