Threads.java

Download Threads.java

  1: /* Threads.java - App to show how to pause and terminate threads
  2:  * without using stop(), suspend(), or resume().
  3:  *
  4:  * Each thread draws a simple animation of the letters of the alphabet
  5:  * and checks for stop requests only once each cycle, on 'Z'.  Suspend
  6:  * requests are processed immediately.  Note the use of synchronized
  7:  * blocks (and methods) whenever a shared variable is accessed, and
  8:  * whenever using wait() or notify().
  9:  *
 10:  * This program was adapted from RandomCharacters.java, pp. 760-762
 11:  * of Deitel & Deitel "Java How to Program", 3rd Ed.  (C) 1999 by
 12:  * Prentice-Hall.
 13:  *
 14:  * Written 2000 by Wayne Pollock, Tampa Florida USA.
 15:  * Modified 2021 to convert from Applet.
 16:  */
 17: 
 18: import java.awt.*;
 19: import java.awt.event.*;
 20: 
 21: public class Threads extends Frame implements ItemListener, Runnable {
 22:    private Thread [] thread;
 23:    private Label [] output;
 24:    private Checkbox [] suspendRequest, stopRequest;
 25:    private boolean [] suspended;  // The actual status.
 26:    private Component me;  // Used to hide the focus, which I think is ugly.
 27:    private static final int NUM_THREADS = 3 + 1;
 28: 
 29:    public static void main ( String[] args ) {
 30:       Frame frame = new Threads();
 31:       frame.setSize(400, 150);
 32:       frame.setTitle("Multi-threading Demo");
 33:       frame.setLocationRelativeTo(null);  // Center on screen
 34:       frame.addWindowListener(new WindowAdapter(){
 35:            public void windowClosing(WindowEvent e) {
 36:               frame.dispose();
 37:            }
 38:       });
 39:       frame.setVisible(true);
 40:    }
 41: 
 42:    public Threads () {
 43:       me = this;
 44:       stopRequest = new Checkbox[ NUM_THREADS ];
 45:       suspendRequest = new Checkbox[ NUM_THREADS ];
 46:       suspended = new boolean[ NUM_THREADS ];
 47:       thread = new Thread[ NUM_THREADS ];
 48:       output = new Label[ NUM_THREADS ];
 49: 
 50:       // Set up GUI:
 51: 
 52:       setLayout( new GridLayout( 0, 3, 5, 5 ) );
 53:       setBackground( Color.cyan );
 54: 
 55:       // For each thread: create, initialize, and add a Label and
 56:       // Checkboxes for syspend and stop requests.
 57: 
 58:       for ( int i = 1; i < NUM_THREADS; ++i )
 59:       {  output[i] = new Label();
 60:            add( output[i] );
 61:            output[i].setText( "Thread " + i + ":   A" );
 62:          suspendRequest[i] = new Checkbox( "Suspend" );
 63:            add( suspendRequest[i] );
 64:            suspended[i] = false;
 65:          stopRequest[i] = new Checkbox( "Stop" );
 66:            add( stopRequest[i] );
 67:          thread[i] = new Thread( this, "" + i );
 68:            thread[i].start();
 69: 
 70:          // Hook up event listeners for the Checkboxes:
 71: 
 72:          suspendRequest[i].addItemListener( this );
 73:          stopRequest[i].addItemListener( new ItemListener ()
 74:            {  public synchronized void itemStateChanged ( ItemEvent e )
 75:               {  if ( e.getStateChange() == ItemEvent.SELECTED )
 76:                     notifyAll();  // wake up suspended threads (if any).
 77:                  me.requestFocus();
 78:               }
 79:            }
 80:          );
 81:       }
 82:    }
 83: 
 84: 
 85:    public synchronized void start ()
 86:    {  for ( int i = 1; i < NUM_THREADS; ++i )
 87:          suspended[i] = suspendRequest[i].getState();
 88:       notifyAll();
 89:       me.requestFocus();  // Hide the focus.
 90:    }
 91: 
 92: 
 93:    public synchronized void stop ()
 94:    {  for ( int i = 1; i < NUM_THREADS; ++i )
 95:          suspended[i] = true;
 96:    }
 97: 
 98:    // Handler for suspendRequest events (not stop events):
 99: 
100:    public synchronized void itemStateChanged ( ItemEvent ie )
101:    {   for ( int i = 1; i < NUM_THREADS; ++i )
102:           if ( ie.getSource() == suspendRequest[i] )
103:              if ( ! suspendRequest[i].getState() )
104:              {  suspended[i] = false;
105:                 notifyAll();  // Wake up all waiting threads (Qu: Why?)
106:              } else
107:              {  suspended[i] = true;
108:              }
109:        me.requestFocus();  // Hide the focus.
110:    }
111: 
112: 
113:    public void run ()
114:    {  String threadName = Thread.currentThread().getName();
115:       int i = 0;  // i is the current Thread's index number (its name too).
116:       int ch = 'A';
117: 
118:       try { i = Integer.parseInt( threadName ); }
119:       catch ( Exception e )
120:       {  System.err.println("Cannot parse Thread " + threadName +
121:             "\n" + e );
122:       }
123: 
124:       // Note tricky method of sequencing ABC...XYZABC...XYZABC...
125:       // Also note how this whole method  could be simplified if
126:       // Thread-local data were used.  Sadly that is not a Java 1.1 feature.
127: 
128: MainLoop:
129:       for ( ;; )
130:       {  // Pause for between 0.5 and 1.0 seconds:
131:          try { Thread.sleep( 500 + (int) (Math.random() * 500) ); }
132:          catch ( InterruptedException e ) {}  // Do nothing.
133: 
134:          synchronized ( this )
135:          {  if ( ch == 'Z' && stopRequest[i].getState() )
136:                break MainLoop;  // Time to quit.
137: 
138:             while ( suspended[i] )
139:             {  output[i].setBackground( Color.lightGray );
140:                try { wait(); } catch ( InterruptedException e ) {}
141:                if ( ch == 'Z' && stopRequest[i].getState() )
142:                   break MainLoop; // stop overrides suspend
143:             }
144:             output[i].setBackground( Color.cyan );
145:          }
146:          ch = ( ((ch-'A') + 1) % 26 ) + 'A';
147:          output[i].setText( "Thread " + i + ":   " + (char) ch );
148:       }
149:       output[i].setBackground( Color.gray );
150:       suspendRequest[i].setEnabled( false );
151:       stopRequest[i].setEnabled( false );
152:    }
153: 
154: } // End of class Threads