CS 4773 Object Oriented Systems
Threads

Code for the programs can be found in
/usr/local/courses/cs4773/spring98/examples/set4


Previous Topic: Layouts

Introduction
Thread States
Ping Pong Application
Using Join
The Ping Applet
Why Use a Thread? (End of class 1/29/98)
Attempting to Paint
Fixing PingBad.java
Using an External Thread
The PingPong Applet
An Object Oriented Version (End of class 2/5/98)
Summary of Applets

Next Topic: A Simple Animation



Introduction

There are two main ways to use threads in Java:

Thread States

When you declare a Thread variable with
Thread ping
the variable is initially null.

If an applet implements Runnable it must have a run method. Executing:
ping = new Thread(this)
creates a new thread which can execute the run method of the applet. The thread can be started with its start method:
ping.start();
At this point the thread is in its active state. It remains active until it is stopped or it completes execution of its run method.

An active thread can be suspended with its suspend method. This delays further execution until its resume method is called. It is not an error to suspend a thread which has not yet started or has completed execution.


Ping Pong Application

PingPong.java

This thread prints out a word a given number of times with a given delay. It also shows the number of active threads.

sleep throws the InterruptedException so it must catch it.

public class PingPong extends Thread {
   String word;                 // what word to print
   int delay;                   // how long to pause
   int count;                   // number of iterations

   PingPong(String What, int Time, int number) {
      word = What;
      delay = Time;
      count = number;
      setName(What);
   }

   public void run() {
      try {
         for(int i=0;i < count;i++) {
            System.out.println(i+": "+word+":"+activeCount());
            sleep(delay);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }  
   }   
}

PingPongTest1.java

This is an application that just starts two copies of PingPong.
class PingPongTest1{

  public static void main (String[] args){
     PingPong ping;
     PingPong pong;

     ping = new PingPong("ping", 2000, 10);
     pong = new PingPong("PONG", 5000, 3);
     ping.start();
     pong.start();
  }
}

Ping Pong Test1 Output

You can obtain the following by executing make run1.
0: ping:3
0: PONG:3
1: ping:3
2: ping:3
1: PONG:3
3: ping:3
4: ping:3
2: PONG:3
5: ping:3
6: ping:3
7: ping:3
8: ping:2
9: ping:2

Using join

Join suspends the caller until the thread has completed.

The following example waits for each thread to complete and then prints a message. It also has a method which shows the threads.

class PingPongTest2{

   public static void show_threads(String msg) {
      Thread[] tlist = new Thread[50];
      int count;
      count = Thread.enumerate(tlist);
      System.out.println(msg + " Number of threads: "+count);
      for (int i=0;i < count;i++)
        System.out.println("    "+i+": "+tlist[i]);
   }

  public static void main (String[] args){
     PingPong ping;
     PingPong pong;

     show_threads("Start of main");
     ping = new PingPong("ping", 2000, 10);
     show_threads("ping created");
     pong = new PingPong("PONG", 3000, 5);
     show_threads("pong created");
     ping.start();
     pong.start();
     try {pong.join();} catch(InterruptedException e) {}
     show_threads("pong joined");
     try {ping.join();} catch(InterruptedException e) {}
     show_threads("ping joined");
  }
}

Join Ping Pong Output

You can obtain the following by executing make run2.
Start of main Number of threads: 1
    0: Thread[main,5,main]
ping created Number of threads: 2
    0: Thread[main,5,main]
    1: Thread[ping,5,main]
pong created Number of threads: 3
    0: Thread[main,5,main]
    1: Thread[ping,5,main]
    2: Thread[PONG,5,main]
0: ping:3
0: PONG:3
1: ping:3
1: PONG:3
2: ping:3
2: PONG:3
3: ping:3
4: ping:3
3: PONG:3
5: ping:3
4: PONG:3
6: ping:3
7: ping:3
pong joined Number of threads: 2
    0: Thread[main,5,main]
    1: Thread[ping,5,main]
8: ping:2
9: ping:2
ping joined Number of threads: 1
    0: Thread[main,5,main]

The Ping Applet

This is a simple applet which displays the word ping 10 times at a rate of once per second. It also outputs a sound.

Pushing the Start button starts the thread which executes the run method of the applet. The thread writes into a TextArea and outputs a sound. Since the thread executes the run method it has access to all of the variables of the applet.

The thread should be suspended when the the browser leaves the page containing the applet (the stop method) and resumed when the page is revisited (the start method). Note the test for a null pointer.

The Start button is disabled when the thread is running.
It is enabled when the thread is done.

When the thread is done a new ping thread is created so that it can be started again.

/* < Applet code = Ping
width = 300 height = 300 >
< /Applet >
*/

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

public class Ping extends Applet implements ActionListener, Runnable {
   TextArea output;
   Button StartButton;
   AudioClip ping_clip;
   Thread ping;

   public void init() {
      setBackground(Color.lightGray);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      output = new TextArea();
      output.setBackground(Color.gray);
      StartButton = new Button("Start");
      StartButton.setBackground(Color.red);
      add("Center",output);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      ping = new Thread(this);
      StartButton.addActionListener(this);
   }

   public void stop() {
      if (ping != null)
         ping.suspend();
   }

   public void start() {
       if (ping != null)
          ping.resume();
   }

   public void run() {
      StartButton.setEnabled(false);
      try {
         for(int i=0;i < 10;i++) {
            ping_clip.play();
            output.append(i+": ping\n");
            Thread.sleep(1000);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      output.append("done\n");
      ping = new Thread(this);   // So thread can be started again
      StartButton.setEnabled(true);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output.append("starting thread\n");
         ping.start();
      }
   }
}
Click Here to run this applet.

Why Use a Thread?

The example PingSimple.java below is almost identical to Ping.java but the Start button calls run directly rather than starting a new thread.
/* < Applet code = PingSimple
     width = 300 height = 300 >
   < /Applet >
*/

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

public class PingSimple extends Applet implements ActionListener {
   TextArea output;
   Button StartButton;
   AudioClip ping_clip;

   public void init() {
      setBackground(Color.lightGray);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      output = new TextArea();
      output.setBackground(Color.gray);
      StartButton = new Button("Start");
      StartButton.setBackground(Color.red);
      add("Center",output);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      StartButton.addActionListener(this);
   }

   public void run() {
      StartButton.setEnabled(false);
      try {
         for(int i=0;i < 10;i++) {
            ping_clip.play();
            output.append(i+": ping\n");
            Thread.sleep(1000);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      output.append("done\n");
      StartButton.setEnabled(true);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output.append("starting thread\n");
         run();
      }
   }
}
It does behave a little differently in that the output continues if the browser leaves the page containing the applet.

Click Here to run this applet.


Attempting to Paint

We can see why a thread is necessary by trying to draw a string graphically rather than relying on a TextArea.

The applet PingBad.java> attempts to update a count of the number of pings after each ping.

/* < Applet code = PingBad
     width = 300 height = 300 >
   < /Applet >
*/

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

public class PingBad extends Applet implements ActionListener {
   TextArea output;
   Button StartButton;
   AudioClip ping_clip;
   int count;

   public void init() {
      setBackground(Color.lightGray);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      output = new TextArea();
      output.setBackground(Color.yellow);
      StartButton = new Button("Start");
      StartButton.setBackground(Color.red);
      add("North",output);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      StartButton.addActionListener(this);
      count = 0;
   }

   public void paint(Graphics g) {
      g.drawString("Ping Count is "+count,20,250);
   }

   public void run() {
      StartButton.setEnabled(false);
      try {
         for(int i=0;i < 10;i++) {
            ping_clip.play();
            output.append(i+": ping\n");
            count++;
            repaint(1);
            Thread.sleep(1000);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      repaint(1);
      output.append("done\n");
      StartButton.setEnabled(true);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output.append("starting thread\n");
         run();
      }
   }
}
The count is only redisplayed when the loop is complete even though repaint(1) is called inside the loop.

Click Here to run this applet.


Fixing PingBad.java

The applet PingFixed.java is almost identical to Ping.java except that it starts a new thread to execute run.
/* < Applet code = PingFixed
     width = 300 height = 300 >
   < /Applet >
*/

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

public class PingFixed extends Applet implements ActionListener, Runnable {
   TextArea output;
   Button StartButton;
   AudioClip ping_clip;
   int count;
   Thread ping;

   public void init() {
      setBackground(Color.lightGray);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      output = new TextArea();
      output.setBackground(Color.yellow);
      StartButton = new Button("Start");
      StartButton.setBackground(Color.red);
      add("North",output);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      ping = new Thread(this);
      StartButton.addActionListener(this);
      count = 0;
   }

   public void stop() {
      if (ping != null)
         ping.suspend();
   }

   public void start() {
       if (ping != null)
          ping.resume();
   }

   public void paint(Graphics g) {
      g.drawString("Ping Count is "+count,20,250);
   }

   public void run() {
      StartButton.setEnabled(false);
      try {
         for(int i=0;i < 10;i++) {
            ping_clip.play();
            output.append(i+": ping\n");
            count++;
            repaint(1);
            Thread.sleep(1000);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      repaint(1);
      output.append("done\n");
      ping = new Thread(this);   // So thread can be started again
      StartButton.setEnabled(true);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output.append("starting thread\n");
         ping.start();
      }
   }
}
Click Here to run this applet.


Here are the differences between PingBad.java and PingFixed.java:
vip% diff PingBad.java PingFixed.java
1c1
< /* < Applet code = PingBad
---
> /* < Applet code = PingFixed
10c10
< public class PingBad extends Applet implements ActionListener {
---
>  public class PingFixed extends Applet implements ActionListener, Runnable {
14a15
>    Thread ping;
26a28
%gt       ping = new Thread(this);
30a33,42
>    public void stop() {
>       if (ping != null)
>         ping.suspend();
>    }
> 
>    public void start() {
>        if (ping != null)
>           ping.resume();
>    }
> 
49a62
>       ping = new Thread(this);   // So thread can be started again
56c69
<          run();
---
>          ping.start();

Using an External Thread

PingExternalThread.java extends Thread and contains the code from the run method of the applet.
In order for this to have access to the methods and variables of the applet, the applet is passed as a parameter.
public class PingExternalThread extends Thread {

   PingExternalApplet ap;

   public PingExternalThread(PingExternalApplet ap) {
      this.ap = ap;
   }

   public void run() {
      ap.StartButton.setEnabled(false);
      try {
         for(int i=0;i < 10;i++) {
            ap.ping_clip.play();
            ap.output.append(i+": ping\n");
            ap.count++;
            ap.repaint(1);
            sleep(1000);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      ap.repaint(1);
      ap.output.append("done\n");
      ap.new_thread();          // So thread can be started again
      ap.StartButton.setEnabled(true);
   }
}
The applet PingExternalApplet.java uses this thread.
It is almost identical to PingFixed without the run method.
/* < Applet code = PingExternalApplet
     width = 300 height = 300 >
   < /Applet >
*/

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

public class PingExternalApplet extends Applet implements ActionListener {
   TextArea output;
   Button StartButton;
   AudioClip ping_clip;
   int count;
   PingExternalThread ping;

   public void init() {
      setBackground(Color.lightGray);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      output = new TextArea();
      output.setBackground(Color.yellow);
      StartButton = new Button("Start");
      StartButton.setBackground(Color.red);
      add("North",output);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      new_thread();
      StartButton.addActionListener(this);
      count = 0;
   }

   void new_thread() {
      ping = new PingExternalThread(this);
   }

   public void stop() {
      if (ping != null)
         ping.suspend();
   }

   public void start() {
       if (ping != null)
          ping.resume();
   }

   public void paint(Graphics g) {
      g.drawString("Ping Count is "+count,20,250);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output.append("starting thread\n");
         ping.start();
      }
   }
}
Click Here to run this applet.


The PingPong Applet

If you want to run two threads it is inconvenient to use the run method of the applet for both since you cannot pass parameters to run.

Here we use an external thread similar to PingExternalThread, but we pass the TextAreas and a clip rather than passing the applet.

The PingPongForApplet

The PingPongForApplet.java thread writes strings to two text areas and outputs a sound. The string contains a count of the active threads.

import java.awt.*;
import java.applet.*;

public class PingPongForApplet extends Thread {
   String word;                 // what word to print
   int delay;                   // how long to pause
   int count;                   // number of iterations
   TextArea area1;
   TextArea area2;
   AudioClip clip;

   PingPongForApplet(String What, int Time, int number, AudioClip clip,
      TextArea area1, TextArea area2) {
      word = What;
      delay = Time;
      count = number;
      this.area1 = area1;
      this.area2 = area2;
      this.clip = clip;
      setName(What);
   }

   public void run() {
      try {
         for(int i=0;i < count;i++) {
            clip.play();
            area1.append(i+": "+word+":"+activeCount()+"\n");
            area2.append(i+": "+word+":"+activeCount()+"\n");
            sleep(delay);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      area1.append(word+" done\n");
      area2.append(word+" done\n");
   }
}
Here is the PPApplet.java applet which starts two of these threads.

Instead of disabling the Start button until the threads are done, it changes it to a Stop button which suspends the threads.

The threads are not automatically resumed if the browser reenters the page containing the applet, the Start button must be pushed first.

/* < Applet code = PPApplet
     width = 600 height = 400 >
   < /Applet >
*/

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

public class PPApplet extends Applet implements ActionListener {
   TextArea output_common;
   TextArea output_1;
   TextArea output_2;
   Button StartButton;
   PingPongForApplet ping;
   PingPongForApplet pong;
   AudioClip ping_clip;
   AudioClip pong_clip;

   public void init() {
      setBackground(Color.cyan);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      p.setLayout(new GridLayout(1,3));
      output_common = new TextArea(20,20);
      output_1 = new TextArea(20,20);
      output_2 = new TextArea(20,20);
      output_common.setBackground(Color.yellow);
      output_1.setBackground(Color.gray);
      output_2.setBackground(Color.gray);
      p.add(output_1);
      p.add(output_common);
      p.add(output_2);
      add("North",p);
      StartButton = new Button("Start");
      StartButton.addActionListener(this);
      StartButton.setBackground(Color.pink);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      pong_clip = getAudioClip(getCodeBase(),"pong.au");
   }

   public void paint(Graphics g) {
      g.drawString("Nothing Useful here.",10,350);
   }

   public void stop() {
      StartButton.setLabel("Start");
      if (ping != null)
         ping.suspend();
      if (pong != null)
         pong.suspend();
   }

   private void start_it() {
      if (ping == null) {
         ping = new PingPongForApplet("ping", 2000, 10, ping_clip,
             output_1,output_common);
         output_common.append("ping started\n");
         output_1.append("ping started\n");
         ping.start();
      }
      else if (ping.isAlive()) {
         output_common.append("ping resumed\n");
         output_1.append("ping resumed\n");
         ping.resume();
      }
      else {
         ping = new PingPongForApplet("ping", 2000, 10, ping_clip,
              output_1,output_common);
         output_common.append("ping restarted\n");
         output_1.append("ping restarted\n");
         ping.start();
      }
      if (pong == null) {
         pong = new PingPongForApplet("PONG", 3000, 5, pong_clip,
             output_2,output_common);
         output_common.append("pong started\n");
         output_1.append("pong started\n");
         pong.start();
      }
      else if (pong.isAlive()) {
         output_common.append("pong resumed\n");
         output_2.append("pong resumed\n");
         pong.resume();
      }
      else {
         pong = new PingPongForApplet("PONG", 3000, 5, pong_clip,
            output_2,output_common);
         output_common.append("pong restarted\n");
         output_2.append("pong restarted\n");
         pong.start();
      }
   }

   void suspend_it() {
      output_1.append("ping suspended\n");
      output_2.append("PONG suspended\n");
      ping.suspend();
      pong.suspend();
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output_common.append("Number of threads: "+Thread.activeCount()+"\n");
         if (StartButton.getLabel().equals("Start")) {
            StartButton.setLabel("Stop");
            output_common.append("Start Button Pushed\n");
            start_it();
         }
         else if (StartButton.getLabel().equals("Stop")) {
            StartButton.setLabel("Start");
            output_common.append("Stop Button Pushed\n");
            suspend_it();
         }

      }
   }
}
The most complicated part of this is the start_it method which attempts to start the two threads.

Click Here to run this applet.


An Object Oriented Version

One of the principles of object oriented programming is to make reusable objects.
The thread above has a very specific interface in which you pass two text areas and an audio clip.

Conceptually, the thread waits and does output. The type of output should be general.

We can instead have the caller provide a method to do the output. You can do this in a straight forward way by passing the caller as an argument as we did in PingExternalThread, but this requires having the thread depend on the class of the caller.

Instead we create an interface which describes the output.
This is the interface DisplayInfo.java.

public interface DisplayInfo {

   public abstract void show_string(int id, String str);
   
}
It has one abstract method. Any class which implements this interface must define this methods. The PingPongThread.java is also very simple.

It is passed an id which can be used to identify the instance of the thread and an object of class DisplayInfo.
DisplayInfo acts as a prototype for its methods and variables so that these can be used by this class.
In this case there is only one method.

import java.awt.*;
import java.applet.*;

public class PingPongThread extends Thread {
   int delay;                   // how long to pause
   int count;                   // number of iterations
   int id;                      // an id for this thread
   String what;                 // String to display
   DisplayInfo info;            // call show_string to display

   PingPongThread(int id, String what, int Time, int number, DisplayInfo info) {
      this.id = id;
      delay = Time;
      count = number;
      this.what = what;
      this.info = info;
      setName(what);
   }

   public void run() {
      try {
         for(int i=0;i < count-1;i++) {
            info.show_string(id,i+": "+what);
            sleep(delay);    // wait until next time
         }
      }  catch (InterruptedException e) {
         return;                 // end this thread
      }
      info.show_string(id,(count-1)+": "+what+"\n"+what+" done");
   }
}
All of the structure of the output is contained in the applet PingPongApplet.java

The only interesting part is the show_string method. It tests the ID of the calling thread and outputs accordingly. It also keeps a count of the number of times it is called with each ID (ping_count and pong_count so that these values can be displayed by the paint method.

   public void show_string(int id, String str) {
      if (id == PINGID) {
         ping_count++;
         ping_clip.play();
         output_common.append(str+":"+Thread.activeCount()+"\n");
         output_1.append(str+":"+Thread.activeCount()+"\n");
      }  
      else if (id == PONGID) {
         pong_count++;
         pong_clip.play();
         output_common.append(str+":"+Thread.activeCount()+"\n");
         output_2.append(str+":"+Thread.activeCount()+"\n");
      }  
      repaint(1);
   }
Here is the complete applet.
/* < Applet code = PingPongApplet
     width = 600 height = 400 >
   < /Applet >
*/

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

public class PingPongApplet extends Applet 
        implements ActionListener, DisplayInfo {
   final int PINGID = 0;
   final int PONGID = 1;
   TextArea output_common;
   TextArea output_1;
   TextArea output_2;
   Button StartButton;
   PingPongThread ping;
   PingPongThread pong;
   AudioClip ping_clip;
   AudioClip pong_clip;
   int ping_count = 0;
   int pong_count = 0;

   public void init() {
      setBackground(Color.cyan);
      Panel p = new Panel();
      setLayout(new BorderLayout());
      p.setLayout(new GridLayout(1,3));
      output_common = new TextArea(20,20);
      output_1 = new TextArea(20,20);
      output_2 = new TextArea(20,20);
      output_common.setBackground(Color.yellow);
      output_1.setBackground(Color.lightGray);
      output_2.setBackground(Color.lightGray);
      p.add(output_1);
      p.add(output_common);
      p.add(output_2);
      add("North",p);
      StartButton = new Button("Start");
      StartButton.addActionListener(this);
      StartButton.setBackground(Color.pink);
      add("South",StartButton);
      ping_clip = getAudioClip(getCodeBase(),"ping.au");
      pong_clip = getAudioClip(getCodeBase(),"pong.au");
   }

   public void paint(Graphics g) {
      g.drawString("ping count = "+ping_count,10,330);
      g.drawString("pong count = "+pong_count,10,345);
      g.drawString("thread count = "+Thread.activeCount(),10,360);
   }

   public void stop() {
      StartButton.setLabel("Start");
      if (ping != null)
         ping.suspend();
      if (pong != null)
         pong.suspend();
   }

   private void start_it() {
      if (ping == null) {
         ping = new PingPongThread(PINGID,"ping", 2000, 10, this);
         output_common.append("ping started\n");
         output_1.append("ping started\n");
         ping.start();
      }
      else if (ping.isAlive()) {
         output_common.append("ping resumed\n");
         output_1.append("ping resumed\n");
         ping.resume();
      }
      else {
         ping = new PingPongThread(PINGID,"ping", 2000, 10, this);
         output_common.append("ping restarted\n");
         output_1.append("ping restarted\n");
         ping.start();
      }
      if (pong == null) {
         pong = new PingPongThread(PONGID,"PONG", 3000, 5, this);
         output_common.append("pong started\n");
         output_1.append("pong started\n");
         pong.start();
      }
      else if (pong.isAlive()) {
         output_common.append("pong resumed\n");
         output_2.append("pong resumed\n");
         pong.resume();
      }
      else {
         pong = new PingPongThread(PONGID,"PONG", 3000, 5, this);
         output_common.append("pong restarted\n");
         output_2.append("pong restarted\n");
         pong.start();
      }
   }

   void suspend_it() {
      output_1.append("ping suspended\n");
      output_2.append("PONG suspended\n");
      ping.suspend();
      pong.suspend();
   }

   public void show_string(int id, String str) {
      if (id == PINGID) {
         ping_count++;
         ping_clip.play();
         output_common.append(str+":"+Thread.activeCount()+"\n");
         output_1.append(str+":"+Thread.activeCount()+"\n");
      }
      else if (id == PONGID) {
         pong_count++;
         pong_clip.play();
         output_common.append(str+":"+Thread.activeCount()+"\n");
         output_2.append(str+":"+Thread.activeCount()+"\n");
      }
      repaint(1);
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == StartButton) {
         output_common.append("Number of threads: "+Thread.activeCount()+"\n");
         if (StartButton.getLabel().equals("Start")) {
            StartButton.setLabel("Stop");
            output_common.append("Start Button Pushed\n");
            start_it();
         }
         else if (StartButton.getLabel().equals("Stop")) {
            StartButton.setLabel("Start");
            output_common.append("Stop Button Pushed\n");
            suspend_it();
         }

      }
   }
}
Click Here to run this applet.


Summary of Applets


Next topic: A Simple Animation