/home/wpollock1/public_html/AJava/Threads.java
/* Threads.java - App to show how to pause and terminate threads
* without using stop(), suspend(), or resume().
*
* Each thread draws a simple animation of the letters of the alphabet
* and checks for stop requests only once each cycle, on 'Z'. Suspend
* requests are processed immediately. Note the use of synchronized
* blocks (and methods) whenever a shared variable is accessed, and
* whenever using wait() or notify().
*
* This program was adapted from RandomCharacters.java, pp. 760-762
* of Deitel & Deitel "Java How to Program", 3rd Ed. (C) 1999 by
* Prentice-Hall.
*
* Written 2000 by Wayne Pollock, Tampa Florida USA.
* Modified 2021 to convert from Applet.
*/
import java.awt.*;
import java.awt.event.*;
public class Threads extends Frame implements ItemListener, Runnable {
private Thread [] thread;
private Label [] output;
private Checkbox [] suspendRequest, stopRequest;
private boolean [] suspended; // The actual status.
private Component me; // Used to hide the focus, which I think is ugly.
private static final int NUM_THREADS = 3 + 1;
public static void main ( String[] args ) {
Frame frame = new Threads();
frame.setSize(400, 150);
frame.setTitle("Multi-threading Demo");
frame.setLocationRelativeTo(null); // Center on screen
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
frame.dispose();
}
});
frame.setVisible(true);
}
public Threads () {
me = this;
stopRequest = new Checkbox[ NUM_THREADS ];
suspendRequest = new Checkbox[ NUM_THREADS ];
suspended = new boolean[ NUM_THREADS ];
thread = new Thread[ NUM_THREADS ];
output = new Label[ NUM_THREADS ];
// Set up GUI:
setLayout( new GridLayout( 0, 3, 5, 5 ) );
setBackground( Color.cyan );
// For each thread: create, initialize, and add a Label and
// Checkboxes for syspend and stop requests.
for ( int i = 1; i < NUM_THREADS; ++i )
{ output[i] = new Label();
add( output[i] );
output[i].setText( "Thread " + i + ": A" );
suspendRequest[i] = new Checkbox( "Suspend" );
add( suspendRequest[i] );
suspended[i] = false;
stopRequest[i] = new Checkbox( "Stop" );
add( stopRequest[i] );
thread[i] = new Thread( this, "" + i );
thread[i].start();
// Hook up event listeners for the Checkboxes:
suspendRequest[i].addItemListener( this );
stopRequest[i].addItemListener( new ItemListener ()
{ public synchronized void itemStateChanged ( ItemEvent e )
{ if ( e.getStateChange() == ItemEvent.SELECTED )
notifyAll(); // wake up suspended threads (if any).
me.requestFocus();
}
}
);
}
}
public synchronized void start ()
{ for ( int i = 1; i < NUM_THREADS; ++i )
suspended[i] = suspendRequest[i].getState();
notifyAll();
me.requestFocus(); // Hide the focus.
}
public synchronized void stop ()
{ for ( int i = 1; i < NUM_THREADS; ++i )
suspended[i] = true;
}
// Handler for suspendRequest events (not stop events):
public synchronized void itemStateChanged ( ItemEvent ie )
{ for ( int i = 1; i < NUM_THREADS; ++i )
if ( ie.getSource() == suspendRequest[i] )
if ( ! suspendRequest[i].getState() )
{ suspended[i] = false;
notifyAll(); // Wake up all waiting threads (Qu: Why?)
} else
{ suspended[i] = true;
}
me.requestFocus(); // Hide the focus.
}
public void run ()
{ String threadName = Thread.currentThread().getName();
int i = 0; // i is the current Thread's index number (its name too).
int ch = 'A';
try { i = Integer.parseInt( threadName ); }
catch ( Exception e )
{ System.err.println("Cannot parse Thread " + threadName +
"\n" + e );
}
// Note tricky method of sequencing ABC...XYZABC...XYZABC...
// Also note how this whole method could be simplified if
// Thread-local data were used. Sadly that is not a Java 1.1 feature.
MainLoop:
for ( ;; )
{ // Pause for between 0.5 and 1.0 seconds:
try { Thread.sleep( 500 + (int) (Math.random() * 500) ); }
catch ( InterruptedException e ) {} // Do nothing.
synchronized ( this )
{ if ( ch == 'Z' && stopRequest[i].getState() )
break MainLoop; // Time to quit.
while ( suspended[i] )
{ output[i].setBackground( Color.lightGray );
try { wait(); } catch ( InterruptedException e ) {}
if ( ch == 'Z' && stopRequest[i].getState() )
break MainLoop; // stop overrides suspend
}
output[i].setBackground( Color.cyan );
}
ch = ( ((ch-'A') + 1) % 26 ) + 'A';
output[i].setText( "Thread " + i + ": " + (char) ch );
}
output[i].setBackground( Color.gray );
suspendRequest[i].setEnabled( false );
stopRequest[i].setEnabled( false );
}
} // End of class Threads