// AccountDemo2.java

// by Mark Weiss, Fall 2002

// Continues AccountDemo1, with monitors added. The withdraw()
// method uses wait() to give up the processor (and monitor) until
// a the deposit() method executes a notifyAll().

// Thus, an OverdraftException is avoided.


class OverdraftException extends Exception
{
    public OverdraftException( ) 
    {
    }
    
    public OverdraftException( String msg )
    {
        super( msg );
    }
}

class Account
{
    public void deposit( int d )
    {
        synchronized( this )
        {
            balance += d;
            System.out.println( "Deposited " + d );
            
            // permits all waiting threads to be rescheduled:
            this.notifyAll( );
        }

    }
    
    public void withdraw( int d ) throws OverdraftException
    {
        try
        {
            synchronized( this )        
            {
					// If wait() is called, this thread gives up the processor
					// and the monitor. It is suspended until notifyAll() is 
					// executed by another thread:
                
                while( balance < d )
                    this.wait( );
                balance -= d;
                System.out.println( "Withdrew " + d );
            }
        }
        catch( InterruptedException e )
        {
            throw new OverdraftException( "Interrupted " + d );
        }
       
    }
    
    private int balance = 0;
}

class WithdrawThread extends Thread
{
    private int amount;
    private Account acc;
    
    public WithdrawThread( Account a, int d )
    {
        amount = d;
        acc = a;
    }

    public void run( )
    {
        try
        {
            acc.withdraw( amount );
        }
        catch( OverdraftException e )
        {
            System.out.println( "Overdraft " + e.getMessage( ) );
        }
    }    
}    


class DepositThread extends Thread
{
    private int amount;
    private Account acc;
    
    public DepositThread( Account a, int d )
    {
        amount = d;
        acc = a;
    }

    public void run( )
    {
        acc.deposit( amount );
    }    
}    

class AccountDemo2
{
    public static void main( String [] args )
    {
        Account acc = new Account( );
        
        Thread t1 = new WithdrawThread( acc, 100 );
        Thread t2 = new WithdrawThread( acc, 75 );
        Thread t3 = new WithdrawThread( acc, 25 );
        Thread t4 = new DepositThread( acc, 200 );
                

        t4.setPriority( Thread.MIN_PRIORITY );
        t4.start( );
        t1.start( );
        t2.start( );
        t3.start( );
    }
}
