Introduction
Thread States
Ping Pong Application
Using Join
The Ping Applet
Why Use a Thread?
Attempting to Paint
Fixing PingBad.java
Using an External Thread
Removing suspend and resume
The Fixed PingExternalApplet
The Fixed PingExternalThead
The PingPong Applet
An Object Oriented Version
A Version That Uses A Canvas
Summary of Applets
Next Topic: A Simple Animation
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.
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 } } }
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(); } }
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
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 showThreads(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; showThreads("Start of main"); ping = new PingPong("ping", 2000, 10); showThreads("ping created"); pong = new PingPong("PONG", 3000, 5); showThreads("pong created"); ping.start(); pong.start(); try {pong.join();} catch(InterruptedException e) {} showThreads("pong joined"); try {ping.join();} catch(InterruptedException e) {} showThreads("ping joined"); } }
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]
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 pingClip; Thread ping; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); Panel p = new Panel(); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(backColor); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); add(BorderLayout.CENTER,output); add(BorderLayout.SOUTH,startButton); pingClip = 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++) { pingClip.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.
/* < 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 pingClip; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); Panel p = new Panel(); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(backColor); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); add(BorderLayout.CENTER,output); add(BorderLayout.SOUTH,startButton); pingClip = getAudioClip(getCodeBase(),"ping.au"); startButton.addActionListener(this); } public void run() { startButton.setEnabled(false); try { for(int i=0;i < 10;i++) { pingClip.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.
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 pingClip; int count; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); Panel p = new Panel(); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(Color.yellow); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); add(BorderLayout.NORTH,output); add(BorderLayout.SOUTH,startButton); pingClip = 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++) { pingClip.play(); output.append(i+": ping\n"); count++; repaint(); Thread.sleep(1000); // wait until next time } } catch (InterruptedException e) { return; // end this thread } repaint(); 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() is called inside the loop.
Click Here to run this applet.
/* < 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 pingClip; int count; Thread ping; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); Panel p = new Panel(); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(Color.yellow); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); add(BorderLayout.NORTH,output); add(BorderLayout.SOUTH,startButton); pingClip = 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++) { pingClip.play(); output.append(i+": ping\n"); count++; repaint(); Thread.sleep(1000); // wait until next time } } catch (InterruptedException e) { return; // end this thread } repaint(); 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.
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; 30a32 > ping = new Thread(this); 34a37,46 > public void stop() { > if (ping != null) > ping.suspend(); > } > > public void start() { > if (ping != null) > ping.resume(); > } > 53a66 > ping = new Thread(this); // So thread can be started again 60c73 < run(); --- > ping.start();
public class PingExternalThread extends Thread { private 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.pingClip.play(); ap.output.append(i+": ping\n"); ap.count++; ap.repaint(); sleep(1000); // wait until next time } } catch (InterruptedException e) { return; // end this thread } ap.repaint(); ap.output.append("done\n"); ap.newThread(); // So thread can be started again ap.startButton.setEnabled(true); } }The applet PingExternalApplet.java uses this thread.
/* < 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; Button suspendButton; Button resumeButton; AudioClip pingClip; int count; PingExternalThread ping; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(Color.yellow); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); suspendButton = new Button("Suspend"); resumeButton = new Button("Resume"); Panel p = new Panel(); p.setLayout(new GridLayout(1,3)); p.add(startButton); p.add(suspendButton); p.add(resumeButton); add(BorderLayout.NORTH,output); add(BorderLayout.SOUTH,p); pingClip = getAudioClip(getCodeBase(),"ping.au"); newThread(); startButton.addActionListener(this); suspendButton.addActionListener(this); resumeButton.addActionListener(this); count = 0; } void newThread() { 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(); } if (e.getSource() == suspendButton) { output.append("suspending thread\n"); stop(); } if (e.getSource() == resumeButton) { output.append("resuming thread\n"); start(); } } }Click Here to run this applet.
What do you think would happen if you pushed Suspend three times and then pushed Resume?
Thread.suspend and Thread.resume were deprecated.
This means that it is suggested that you do not use them and that they may be removed in a later version of Java.
The problems with these methods are related to the way Java does synchronication and will be discussed later.
For now, I will just present you the correct way of doing this so that you have some examples of correct code.
/* < Applet code = PingExternalAppletSuspend width = 300 height = 300 > < /Applet > */ import java.applet.*; import java.awt.*; import java.awt.event.*; public class PingExternalAppletSuspend extends Applet implements ActionListener { TextArea output; Button startButton; Button suspendButton; Button resumeButton; AudioClip pingClip; int count; PingExternalThreadSuspend ping; Color backColor = Color.lightGray; Color foreColor = Color.black; public void init() { setBackground(backColor); setForeground(foreColor); setLayout(new BorderLayout()); output = new TextArea(); output.setBackground(Color.yellow); output.setForeground(foreColor); startButton = new Button("Start"); startButton.setBackground(Color.red); suspendButton = new Button("Suspend"); resumeButton = new Button("Resume"); Panel p = new Panel(); p.setLayout(new GridLayout(1,3)); p.add(startButton); p.add(suspendButton); p.add(resumeButton); add(BorderLayout.NORTH,output); add(BorderLayout.SOUTH,p); pingClip = getAudioClip(getCodeBase(),"ping.au"); newThread(); startButton.addActionListener(this); suspendButton.addActionListener(this); resumeButton.addActionListener(this); count = 0; } void newThread() { ping = new PingExternalThreadSuspend(this); } public void stop() { if (ping != null) ping.setSuspend(true); } public void start() { if (ping != null) ping.setSuspend(false); } 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(); } if (e.getSource() == suspendButton) { output.append("suspending thread\n"); stop(); } if (e.getSource() == resumeButton) { output.append("resuming thread\n"); start(); } } }
If setSuspend is called with parameter false it will wake up the thread using notify().
public class PingExternalThreadSuspend extends Thread { private PingExternalAppletSuspend ap; private boolean suspendFlag; public PingExternalThreadSuspend(PingExternalAppletSuspend ap) { this.ap = ap; } public void run() { ap.startButton.setEnabled(false); try { for(int i=0;i < 10;i++) { ap.pingClip.play(); ap.output.append(i+": ping\n"); ap.count++; ap.repaint(); sleep(1000); // wait until next time checkSuspend(); } } catch (InterruptedException e) { return; // end this thread } ap.repaint(); ap.output.append("done\n"); ap.newThread(); // So thread can be started again ap.startButton.setEnabled(true); } synchronized private void checkSuspend() { try { while (suspendFlag) wait(); } catch (InterruptedException e) { return; } } synchronized public void setSuspend(boolean f) { suspendFlag = f; if (!suspendFlag) notify(); } }
Click Here to run this applet.
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; private boolean suspendFlag = false; 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; try { setName(what); } catch (SecurityException e) { System.out.println("Warning: could not set the name of thread "+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 checkSuspend(); } } catch (InterruptedException e) { return; // end this thread } area1.append(word+" done\n"); area2.append(word+" done\n"); } synchronized private void checkSuspend() { try { while (suspendFlag) wait(); } catch (InterruptedException e) { return; } } synchronized public void setSuspend(boolean f) { suspendFlag = f; if (!suspendFlag) notify(); } }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 (using the new method rather than by calling suspend 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 outputCommon; TextArea outputPing; TextArea outputPong; Button startButton; PingPongForApplet ping; PingPongForApplet pong; AudioClip pingClip; AudioClip pongClip; Color backColor = Color.lightGray; Color foreColor = Color.black; boolean useActive = true; public void init() { if (getParameter("inhibitactive") != null) useActive = false; setBackground(backColor); setForeground(foreColor); Panel q = new Panel(); setLayout(new BorderLayout()); q.setLayout(new GridLayout(1,3)); outputCommon = new TextArea(18,18); outputPing = new TextArea(18,18); outputPong = new TextArea(18,18); outputCommon.setBackground(Color.yellow); outputPing.setBackground(Color.cyan); outputPong.setBackground(Color.cyan); outputCommon.setForeground(foreColor); outputPing.setForeground(foreColor); outputPong.setForeground(foreColor); q.add(outputPing); q.add(outputCommon); q.add(outputPong); add(BorderLayout.NORTH,q); startButton = new Button("Start"); startButton.addActionListener(this); startButton.setBackground(Color.pink); add(BorderLayout.SOUTH,startButton); pingClip = getAudioClip(getCodeBase(),"ping.au"); pongClip = 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.setSuspend(true); if (pong != null) pong.setSuspend(false); } private void startThreads() { if (ping == null) { ping = new PingPongForApplet("ping", 2000, 10, pingClip, outputPing,outputCommon); outputCommon.append("ping started\n"); outputPing.append("ping started\n"); ping.start(); } else if (ping.isAlive()) { outputCommon.append("ping resumed\n"); outputPing.append("ping resumed\n"); ping.setSuspend(false); } else { ping = new PingPongForApplet("ping", 2000, 10, pingClip, outputPing,outputCommon); outputCommon.append("ping restarted\n"); outputPing.append("ping restarted\n"); ping.start(); } if (pong == null) { pong = new PingPongForApplet("PONG", 3000, 5, pongClip, outputPong,outputCommon); outputCommon.append("pong started\n"); outputPong.append("pong started\n"); pong.start(); } else if (pong.isAlive()) { outputCommon.append("pong resumed\n"); outputPong.append("pong resumed\n"); pong.setSuspend(false); } else { pong = new PingPongForApplet("PONG", 3000, 5, pongClip, outputPong,outputCommon); outputCommon.append("pong restarted\n"); outputPong.append("pong restarted\n"); pong.start(); } } private String getActiveCount() { if (useActive) return ": "+Thread.activeCount(); return ""; } void suspendThreads() { outputPing.append("ping suspended\n"); outputPong.append("PONG suspended\n"); ping.setSuspend(true); pong.setSuspend(true); } public void actionPerformed(ActionEvent e) { if (e.getSource() == startButton) { if (useActive) outputCommon.append("Number of threads"+getActiveCount()+"\n"); if (startButton.getLabel().equals("Start")) { startButton.setLabel("Stop"); outputCommon.append("Start Button Pushed\n"); startThreads(); } else if (startButton.getLabel().equals("Stop")) { startButton.setLabel("Start"); outputCommon.append("Stop Button Pushed\n"); suspendThreads(); } } } }The most complicated part of this is the startThreads method which attempts to start the two threads.
Click Here to run this applet.
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 void showString(int id, String str); }It has one method. Any class which implements this interface must define this method.
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 { private int delay; // how long to pause private int count; // number of iterations private int id; // an id for this thread private String what; // String to display private DisplayInfo info; // call showString to display private boolean suspendFlag; // true if thread is to be suspended PingPongThread(int id, String what, int time, int number, DisplayInfo info) { this.id = id; delay = time; count = number; this.what = what; this.info = info; try { setName(what); } catch (SecurityException e) { System.out.println("Warning: could not set the name of thread "+what); } } public void run() { try { for(int i=0;i < count-1;i++) { info.showString(id,i+": "+what); sleep(delay); // wait until next time checkSuspend(); } } catch (InterruptedException e) { return; // end this thread } info.showString(id,(count-1)+": "+what+"\n"+what+" done"); } synchronized private void checkSuspend() { try { while (suspendFlag) wait(); } catch (InterruptedException e) { return; } } synchronized public void setSuspend(boolean f) { suspendFlag = f; if (!suspendFlag) notify(); } }All of the structure of the output is contained in the applet PingPongApplet.java
The only interesting part is the showString 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 (pingCount and pongCount) so that these values can be displayed by the paint method.
public void showString(int id, String str) { if (id == PINGID) { pingCount++; pingClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPing.append(str+getActiveCount()+"\n"); } else if (id == PONGID) { pongCount++; pongClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPong.append(str+getActiveCount()+"\n"); } repaint(); }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 outputCommon; TextArea outputPing; TextArea outputPong; Button startButton; PingPongThread ping; PingPongThread pong; AudioClip pingClip; AudioClip pongClip; int pingCount = 0; int pongCount = 0; Color backColor = Color.lightGray; Color foreColor = Color.black; Panel outputPanel; boolean useActive = true; public void init() { if (getParameter("inhibitactive") != null) useActive = false; setBackground(backColor); setForeground(foreColor); outputPanel = new Panel(); setLayout(new BorderLayout()); outputPanel.setLayout(new GridLayout(1,3)); outputCommon = new TextArea(18,18); outputPing = new TextArea(18,18); outputPong = new TextArea(18,18); outputCommon.setBackground(Color.yellow); outputPing.setBackground(Color.cyan); outputPong.setBackground(Color.cyan); outputCommon.setForeground(foreColor); outputPing.setForeground(foreColor); outputPong.setForeground(foreColor); outputPanel.add(outputPing); outputPanel.add(outputCommon); outputPanel.add(outputPong); add(BorderLayout.NORTH,outputPanel); startButton = new Button("Start"); startButton.addActionListener(this); startButton.setBackground(Color.pink); add(BorderLayout.SOUTH,startButton); pingClip = getAudioClip(getCodeBase(),"ping.au"); pongClip = getAudioClip(getCodeBase(),"pong.au"); } public void paint(Graphics g) { Insets insets; int starty; insets = getInsets(); starty = insets.top + outputPanel.getBounds().height + 15; g.drawString("ping count = "+pingCount,10,starty); g.drawString("pong count = "+pongCount,10,starty + 15); if (useActive) g.drawString("thread count"+getActiveCount(),10,starty + 30); } public void stop() { startButton.setLabel("Start"); if (ping != null) ping.setSuspend(true); if (pong != null) pong.setSuspend(true); } private void startThreads() { if (ping == null) { ping = new PingPongThread(PINGID,"ping", 2000, 10, this); outputCommon.append("ping started\n"); outputPing.append("ping started\n"); ping.start(); } else if (ping.isAlive()) { outputCommon.append("ping resumed\n"); outputPing.append("ping resumed\n"); ping.setSuspend(false); } else { ping = new PingPongThread(PINGID,"ping", 2000, 10, this); outputCommon.append("ping restarted\n"); outputPing.append("ping restarted\n"); ping.start(); } if (pong == null) { pong = new PingPongThread(PONGID,"PONG", 3000, 5, this); outputCommon.append("pong started\n"); outputPong.append("pong started\n"); pong.start(); } else if (pong.isAlive()) { outputCommon.append("pong resumed\n"); outputPong.append("pong resumed\n"); pong.setSuspend(false); } else { pong = new PingPongThread(PONGID,"PONG", 3000, 5, this); outputCommon.append("pong restarted\n"); outputPong.append("pong restarted\n"); pong.start(); } } void suspendThreads() { outputPing.append("ping suspended\n"); outputPong.append("PONG suspended\n"); ping.setSuspend(true); pong.setSuspend(true); } private String getActiveCount() { if (useActive) return ": "+Thread.activeCount(); return ""; } public void showString(int id, String str) { if (id == PINGID) { pingCount++; pingClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPing.append(str+getActiveCount()+"\n"); } else if (id == PONGID) { pongCount++; pongClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPong.append(str+getActiveCount()+"\n"); } repaint(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == startButton) { if (useActive) outputCommon.append("Number of threads"+getActiveCount()+"\n"); if (startButton.getLabel().equals("Start")) { startButton.setLabel("Stop"); outputCommon.append("Start Button Pushed\n"); startThreads(); } else if (startButton.getLabel().equals("Stop")) { startButton.setLabel("Start"); outputCommon.append("Stop Button Pushed\n"); suspendThreads(); } } } }Click Here to run this applet.
/* < Applet code = PingPongAppletCanvas width = 600 height = 400 > < /Applet > */ import java.applet.*; import java.awt.*; import java.awt.event.*; class MyCanvas extends Canvas { private Image buffer; void setBuffer(Image buffer) { this.buffer = buffer; } public void paint(Graphics g) { if (buffer == null) return; g.drawImage(buffer,0,0,null); } } public class PingPongAppletCanvas extends Applet implements ActionListener, DisplayInfo { final int PINGID = 0; final int PONGID = 1; TextArea outputCommon; TextArea outputPing; TextArea outputPong; Button startButton; PingPongThread ping; PingPongThread pong; AudioClip pingClip; AudioClip pongClip; int pingCount = 0; int pongCount = 0; Color backColor = Color.lightGray; Color foreColor = Color.black; MyCanvas mc; Image buffer = null; Graphics gc; int imageWidth = 0; int imageHeight = 0; int currentWidth = 0; int currentHeight = 0; boolean useActive = true; public void init() { if (getParameter("inhibitactive") != null) useActive = false; setBackground(backColor); setForeground(foreColor); Panel outputPanel = new Panel(); setLayout(new BorderLayout()); outputPanel.setLayout(new GridLayout(1,3)); outputCommon = new TextArea(18,18); outputPing = new TextArea(18,18); outputPong = new TextArea(18,18); outputCommon.setBackground(Color.yellow); outputPing.setBackground(Color.cyan); outputPong.setBackground(Color.cyan); outputCommon.setForeground(foreColor); outputPing.setForeground(foreColor); outputPong.setForeground(foreColor); outputPanel.add(outputPing); outputPanel.add(outputCommon); outputPanel.add(outputPong); add(BorderLayout.NORTH,outputPanel); startButton = new Button("Start"); startButton.addActionListener(this); startButton.setBackground(Color.pink); add(BorderLayout.SOUTH,startButton); mc = new MyCanvas(); add(BorderLayout.CENTER,mc); pingClip = getAudioClip(getCodeBase(),"ping.au"); pongClip = getAudioClip(getCodeBase(),"pong.au"); } private void setupImages() { if ( (buffer != null) && (getBounds().width == currentWidth) && (getBounds().height == currentHeight) ) return; imageWidth = mc.getBounds().width; imageHeight = mc.getBounds().height; if ( (imageWidth <= 0) || (imageHeight <= 0) ) return; buffer = createImage(imageWidth,imageHeight); mc.setBuffer(buffer); currentWidth = getBounds().width; currentHeight = getBounds().height; gc = buffer.getGraphics(); } private void fillBuffer() { gc.setColor(Color.green); gc.fillRect(0,0,imageWidth,imageHeight); gc.setColor(Color.black); gc.drawString("ping count = "+pingCount,10,20); gc.drawString("pong count = "+pongCount,10,35); if (useActive) gc.drawString("thread count"+getActiveCount(),10,50); } public void paint(Graphics g) { setupImages(); fillBuffer(); mc.repaint(); } public void start() { repaint(); } public void stop() { startButton.setLabel("Start"); if (ping != null) ping.setSuspend(true); if (pong != null) pong.setSuspend(true); } private void startThreads() { if (ping == null) { ping = new PingPongThread(PINGID,"ping", 2000, 10, this); outputCommon.append("ping started\n"); outputPing.append("ping started\n"); ping.start(); } else if (ping.isAlive()) { outputCommon.append("ping resumed\n"); outputPing.append("ping resumed\n"); ping.setSuspend(false); } else { ping = new PingPongThread(PINGID,"ping", 2000, 10, this); outputCommon.append("ping restarted\n"); outputPing.append("ping restarted\n"); ping.start(); } if (pong == null) { pong = new PingPongThread(PONGID,"PONG", 3000, 5, this); outputCommon.append("pong started\n"); outputPong.append("pong started\n"); pong.start(); } else if (pong.isAlive()) { outputCommon.append("pong resumed\n"); outputPong.append("pong resumed\n"); pong.setSuspend(false); } else { pong = new PingPongThread(PONGID,"PONG", 3000, 5, this); outputCommon.append("pong restarted\n"); outputPong.append("pong restarted\n"); pong.start(); } } void suspendThreads() { outputPing.append("ping suspended\n"); outputPong.append("PONG suspended\n"); ping.setSuspend(true); pong.setSuspend(true); } private String getActiveCount() { if (useActive) return ": "+Thread.activeCount(); return ""; } public void showString(int id, String str) { if (id == PINGID) { pingCount++; pingClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPing.append(str+getActiveCount()+"\n"); } else if (id == PONGID) { pongCount++; pongClip.play(); outputCommon.append(str+getActiveCount()+"\n"); outputPong.append(str+getActiveCount()+"\n"); } repaint(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == startButton) { if (useActive) outputCommon.append("Number of threads"+getActiveCount()+"\n"); if (startButton.getLabel().equals("Start")) { startButton.setLabel("Stop"); outputCommon.append("Start Button Pushed\n"); startThreads(); } else if (startButton.getLabel().equals("Stop")) { startButton.setLabel("Start"); outputCommon.append("Stop Button Pushed\n"); suspendThreads(); } } } }