Download this source file


/*
   <APPLET CODE="Bank" HEIGHT="210" WIDTH="380">
   </APPLET>
*/
// Bank.java - A demo of synchronized threads.
// This program starts two threads, one that transfer $100 from
// checking to savings and back again.  The other transfers $10
// back and forth.  If the "synchronized" keyword is commented
// out from transfer(), the balances will eventually be wrong.
//
// (C)2000 by Wayne Pollock, Tampa Florida USA.  All Rights Reserved.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Bank extends Applet implements Runnable, ActionListener
{
   private Account checking, savings;
   private volatile boolean demoRunning;
   private Button btn;
   private TextField checkingTF, savingsTF;
   private Panel errorP;
   private static final int totalBal = 1000;
   private volatile Thread t1, t2;
   private static final int t1TransferAmount = 100;
   private static final int t2TransferAmount = 10;


   public void init ()
   {
      checking = new Account( totalBal / 2 );
      savings = new Account( totalBal - checking.bal );
      demoRunning = false;

      checkingTF = new TextField( 7 );
        checkingTF.setEditable( false );
        checkingTF.setText( " $" + checking.bal );
        checkingTF.setFont( new Font("Monospaced", Font.PLAIN, 14) );

      savingsTF = new TextField( 7 );
        savingsTF.setEditable( false );
        savingsTF.setText( " $" + savings.bal );
        savingsTF.setFont( new Font("Monospaced", Font.PLAIN, 14) );

      btn = new Button( "Start" );  // This is also the stop button.
        btn.setBackground( Color.green.brighter() );

      Label warning = new Label( "Balance Error!", Label.CENTER );
        warning.setFont( new Font( "SansSerif", Font.BOLD, 24 ) );
        warning.setForeground( Color.red );

      Label title = new Label( "synchronized Thread Demo",
                               Label.CENTER );
        title.setFont( new Font( "SansSerif", Font.BOLD, 24 ) );

      Label footer = new Label( "\u00A9 2000 by Wayne Pollock, "
          + "Tampa FL USA.  All Rights Reserved.", Label.CENTER );
        footer.setFont( new Font( "SansSeriff", Font.PLAIN, 10 ) );

      // Layout the components:

      setLayout( new BorderLayout() );
      setBackground( Color.lightGray );
      Panel wrapper;  // Used so buttons and things won't stretch.

      add( title, "North" );

      Panel checkingP = new Panel();
        checkingP.setLayout( new BorderLayout() );
        checkingP.add( new Label( "Checking Bal", Label.CENTER ),
                       "North" );
        checkingP.add( checkingTF, "South" );
        wrapper = new Panel();
          wrapper.add( checkingP );
      add( wrapper, "West" );

      Panel savingsP = new Panel();
        savingsP.setLayout( new BorderLayout() );
        savingsP.add( new Label( "Savings Bal", Label.CENTER ),
                         "North" );
        savingsP.add( savingsTF, "South" );
        wrapper = new Panel();
          wrapper.add( savingsP );
      add( wrapper, "East" );

      errorP = new Panel();
        errorP.setLayout( new BorderLayout() );
        errorP.setVisible( false );
        errorP.add( new Label( " " ), "North" );  // A Spacer.
        errorP.add( warning, "Center" );
        Button resetBtn = new Button( " Reset " );
          resetBtn.setBackground( Color.orange );
        wrapper = new Panel();
          wrapper.add( resetBtn );
        errorP.add( wrapper, "South" );
      add( errorP, "Center" );

      Panel bot = new Panel();
        bot.setLayout( new BorderLayout() );
        wrapper = new Panel();
          wrapper.add( btn );
        bot.add( wrapper, "North" );
        bot.add( new Label( " " ), "Center" );  // A spacer.
        bot.add( footer, "South" );
      add( bot, "South" );

      errorP.requestFocus();  // Hide the focus.

      // Hook up event listeners:
      btn.addActionListener( this );
      resetBtn.addActionListener( new ActionListener ()
        {  public void actionPerformed ( ActionEvent e )
           {   reset();  }
        }
      );
   }


   public void start ()
   {
      // Create threads:

      t1 = new Thread( this, "thread1" );
      t2 = new Thread( this, "thread2" );
      t1.start();
      t2.start();
   }


   public void stop ()
   {
      t1 = t2 = null;  // The threads will check this and die.
   }


   private void reset ()
   {
      errorP.setVisible( false );
      checking.bal = totalBal / 2;
      savings.bal = totalBal - checking.bal;
      checkingTF.setText( " $" + checking.bal );
      savingsTF.setText( " $" + savings.bal );
      btn.setLabel( "Start" );
      btn.setBackground( Color.green.brighter() );
      btn.setEnabled( true );
      errorP.requestFocus();  // Hide the focus.
      demoRunning = false;
      start();
   }


   public synchronized void actionPerformed ( ActionEvent e )
   {
      if ( demoRunning ) // then turn it off:
      {
         btn.setLabel( "Start" );
         btn.setBackground( Color.green.brighter() );
      } else {
         btn.setLabel( "Stop " );
         btn.setBackground( Color.red );
      }

      // Threads will check this flag and suspend themselves:
      demoRunning = ! demoRunning;
      notifyAll();  // Wake up sleeping threads.
      errorP.requestFocus();  // Hide the focus.
   }


   public void run ()
   {
      Thread me = Thread.currentThread();
      int amount;
      boolean toChecking = true;  // Which way to transfer funds.

      if ( me == t1 )
         amount = t1TransferAmount;
      else
         amount = t2TransferAmount;

      while ( me == t1 || me == t2 )  // else time to terminate!
      {
         try
         {  // Pause for 0.1 seconds up to 1.0 seconds:
            Thread.sleep( (int) ( Math.random() * 900 ) + 100 );

            // Check if demo is suspended (or terminated) yet:
            synchronized( this )
            {  while ( !demoRunning && ( me == t1 || me == t2 ) )
                  wait();
            }
         }
         catch ( InterruptedException ignored ) { }

         if ( me != t1 && me != t2 )  // Check for terminate again.
            break;

         if ( toChecking )
            transfer( checking, savings, amount );
         else
            transfer( savings, checking, amount );
         toChecking = ! toChecking;

         synchronized( this )
         {
            checkingTF.setText( " $" + checking.bal );
            savingsTF.setText( " $" + savings.bal );
            if ( checking.bal + savings.bal != totalBal )
            {  errorP.setVisible( true );
               stop();
               btn.setEnabled( false ); // Disable Start/Stop btn
               validate();
            }
         }
      }
   }


   // Try the effect of commenting out "synchronized"!
   // (The sleep statement just increases the probability of error.)

   private /*synchronized*/ void transfer ( Account toAccount,
                                            Account fromAccount,
                                            int amount )
   {
      int bal = fromAccount.bal;
      pause();
      bal -= amount;
      fromAccount.bal = bal;

      bal = toAccount.bal;
      pause();
      bal  += amount;
      toAccount.bal = bal;
   }


   private void pause ()
   {
      if ( Math.random() < 0.35 )
         try {  Thread.sleep( 10 ); } catch ( Exception e ) {}
   }


   private static class Account
   {
      public int bal;

      Account ( int initialBalance )
      {  bal = initialBalance; }
   }

}  // End of class Bank




Send comments and mail to the WebMaster.