// This code example is from the following source:
//
// Book Title:  Programming with Objects, A Comparative Presentation
//              of Object-Oriented Programming with C++ and Java
//
// Chapter:     Chapter 18  Multithreaded Object-Oriented Programming
//
// Section:     Section 18.6  Java's wait-notify Mechanism For Dealing With Deadlock
//
// The links to the rest of the code in this book are at
//     
//      http://programming-with-objects.com/pwocode.html
//
// For further information regarding the book, please visit
//
//      http://programming-with-objects.com
//



//MultiCustomerAccount.java

///////////////////////////  class Account  ///////////////////////////
class Account {                                                   //(A)
    int balance;                                                  //(B)

    Account() { balance = 0; }                                    //(C)

    synchronized void deposit( int dep ){                         //(D)
        balance += dep;                                           //(E)
        notifyAll();                                              //(F)
    }
    synchronized void withdraw( int draw ) {                      //(G)
        while ( balance < draw ) {                                //(H)
            try { 
                wait();                                           //(I)
            } catch( InterruptedException e ) {}
        }
        balance -= draw;                                          //(J)
    }
}

//////////////////////////  class Depositor  //////////////////////////
class Depositor extends Thread {                                  //(K)
    private Account acct;                                         //(L)

    Depositor( Account act ){ acct = act; }                       //(M)

    public void run() {                                           //(N)
        int i = 0;
        while ( true ) {                                          //(O)
            int x = (int) ( 10 * Math.random() );                 //(P)
            acct.deposit( x );                                    //(Q)
            if ( i++ % 1000 == 0 )                                //(R)
                System.out.println( 
                    "balance after deposits:  " 
                    + acct.balance );                             //(S)
            try { sleep( 5 ); } catch( InterruptedException e ) {}
        }
    }
}

//////////////////////////  class Withdrawer  /////////////////////////
class Withdrawer extends Thread {                                 //(T)
    private Account acct;

    Withdrawer( Account act ) { acct = act; }

    public void run() {
        int i = 0;
        while ( true ) {
            int x = (int) ( 10 * Math.random() );
            acct.withdraw( x );
            if ( i++ % 1000 == 0 ) 
                System.out.println( "balance after withdrawals:  " 
                                 + acct.balance );       
            try { sleep( 5 ); } catch( InterruptedException e ) {}      
        }
    }
}

////////////////////  class MultiCustomerAccount  /////////////////////
class MultiCustomerAccount {                                      //(U)
    public static void main( String[] args ) {
        Account account = new Account();
        Depositor[] depositors = new Depositor[ 5 ];
        Withdrawer[] withdrawers = new Withdrawer[ 5 ];    
        for ( int i=0; i < 5; i++ ) {
            depositors[ i ] = new Depositor( account );      
            withdrawers[ i ] = new Withdrawer( account );
            depositors[ i ].start();
            withdrawers[ i ].start();
        }
    }
}