/home/wpollock1/public_html/AJava/Bank.java

// Bank.java - A demo of (un)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 the transfer() method, the balances will eventually
// be wrong and the demo will stop.
//
// Written 2000 by Wayne Pollock, Tampa Florida USA.
// Updated 5/2021: Converted from Applet, updated style.

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

public class Bank extends Frame implements Runnable, ActionListener {
    private Account checking, savings;
    private volatile boolean demoRunning;
    private static final int totalBal = 1000;
    private volatile Thread t1, t2;
    private static final int t1TransferAmount = 100;
    private static final int t2TransferAmount = 10;

    private Button btn;
    private TextField checkingTF, savingsTF;
    private Panel errorP;

    public static void main ( String[] args ) {
        Frame f = new Bank();
        f.setTitle( "Banking Transfer Demo" );
        f.setSize(430, 260);
        f.setLocationRelativeTo( null );
        f.addWindowListener( new WindowAdapter() {
            @Override public void windowClosing( WindowEvent we ) {
                System.exit(0);
            }
        });
        f.setVisible( true );
    }

    public Bank () {
        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( "(Un)Synchronized Thread Demo",
                                      Label.CENTER );
          title.setFont( new Font( "SansSerif", Font.BOLD, 24 ) );

        Label footer = new Label( "\u00A9 2021 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( ( ActionEvent e ) -> { reset(); } );
        start();  //  Create and run the two threads
    }

    public void start () {
        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