/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