/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