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( "Deposit " + d );
            this.notifyAll( );  // don't use notify!!
        }

    }
    
    public void withdraw( int d ) throws OverdraftException
    {
        try
        {
            synchronized( this )        
            {
                while ( balance < d )
                {
                    System.out.println( "withdraw " + d + " enters wait");
                    this.wait();
                    System.out.println( "withdraw " + d + " back from 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 AccountDemo3
{
    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, 20 );
        Thread t5 = new DepositThread( acc, 180 );
                
        t1.start( );
        t2.start( );
        t3.start( );
        
        t4.setPriority( Thread.MIN_PRIORITY );
        t4.start( );      
        try
        {
            Thread.sleep( 2000 );
        }
        catch( InterruptedException e )
        {
        }
        
        t5.setPriority( Thread.MIN_PRIORITY );
        t5.start( );
    }
}
