Chapter 12

Moving Up to Multithreading


CONTENTS

Multithreading means that a program is divided into separate processes, each of which is then executed individually, but simultaneously. Operating systems use multiprocessing, wherein each program being executed can be thought of as an individual process. When a program is divided, each process is commonly referred to as a thread. The difference between multiprocessing on an operating system level and multiprocessing on a program level is that threads of a program are executed in the same program space. Thus, all threads of a program have access to the same variables and memory space.

This chapter covers how to use the Thread class to execute tasks simultaneously by declaring threads that perform specific tasks. The task executed by the thread can either be part of the applet, with the implementation of the run method, or by declaring a Thread-derived class. This chapter shows how these tasks can communicate effectively with each other. In addition, threads can be grouped and prioritized using the ThreadGroup class.

Threads

Threads are very useful in doing a number of tasks simultaneously. Long or involved processing is typically off-loaded to a thread so that the normal processing can occur with the user. This off-loaded processing can include loading an image into memory to be displayed immediately, queuing up potentially displayable images, playing audio, or any other task that can happen asynchronously to the user's actions. In fact, among other system threads is the garbage collecting thread that takes care of freeing unused memory.

Threads are implemented in Java using the Runnable interface. One of the most difficult concepts to handle when working with threaded applications is to control access to critical sections of code. With Java, this support is built directly into the language with synchronization. Since synchronization is a blocking process, additional functionality is provided to control the locks implemented by the synchronization.

Runnable and Threads

The basis for threads in Java is the Runnable interface, which simply consists of a single method:

public interface Runnable
{
    public abstract void run();
}

There are two ways to create user-defined threads in an applet: implementing the Runnable interface in a class and passing an instance of the Runnable object to an instance of the Thread class, or deriving from the Thread class. Deriving from the Thread class is a viable option because the Thread class is itself a Runnable object.

Implementing the Runnable interface requires the addition of a single function to a class. Consider the following class implementing the Runnable interface:

class MyApplet extends Applet implements Runnable
{
    protected Thread myOtherPersonality;

    public void init()
    {
        myOtherPersonality = new Thread(this);
    }

    public void run()
    {
        // this is where my other personality takes over
        ...
    }
}

This Applet-derived class creates a new thread in the init method, passing the instance of the applet to the thread. Therefore, the thread will use the run method of the applet.

Alternately, deriving from the Thread class involves overriding the run method. Here is the same example implemented with a class derived from Thread:

class MyApplet extends Applet
{
    protected MyThread myOtherPersonality;

    public void init()
    {
        myOtherPersonality = new MyThread();
    }
}

class MyThread extends Thread
{
    public void run()
    {
        // this is where my other personality takes over
        ...
    }
}

The run method was moved from the Applet class to the MyThread class.

These classes show only the basic differences between the two implementations. However, this example does use the Thread constructor that takes a Runnable object as a parameter. Table 12.1 shows all of the Thread constructors that do not take a ThreadGroup instance in the constructor. Constructors involving ThreadGroups will be covered in a later section.

Table 12.1. Public constructors of the Thread class callable without a ThreadGroup.

MemberPurpose
Thread()Creates a thread with a default name.
Thread(Runnable)Creates a thread, with a default name, that will call the Runnable object's run method.
Thread(Runnable, String)Creates a named thread that will call the Runnable object's run method.
Thread(String)Creates a named thread.

The execution of a thread consists of three distinct parts: start, run, and stop. Every thread must be started using the Thread.start method. Once this method is called, the Java Virtual Machine calls the appropriate run method. The intent of the run method is that it will run until either the thread is interrupted, or the thread runs to completion. Thread.stop is used to stop a thread. Table 12.2 shows the methods used to control the execution of a thread.

Table 12.2. Thread methods used to control execution.

MemberPurpose
resume()Resumes execution of a thread after it has been suspended.
Run()Contains the code to be executed while the thread is running.
Start()Starts execution of a thread.
Stop()Stops execution of a thread.
Stop(Throwable)Uses the throwable object to stop the execution of a thread.
Suspend()Momentarily stops the execution of a thread. To restart the thread, call the resume method.

The typical methods in which threads are started and stopped are Applet.start and Applet.stop. Threads should be started and stopped in these locations so that when a page is exited, the threads associated with the applet are also stopped.

Additional functionality can also be found in the Thread class. Table 12.3 shows the remainder of the public methods of the Thread class.

Table 12.3. Additional public methods of the Thread class.

MemberPurpose
activeCount()Returns the number of active threads in the current thread's ThreadGroup instance.
checkAccess()Checks that permissions are granted for the current thread to modify this thread.
countStackFrames()Returns the number of stack frames associated with this thread.
currentThread()Returns the currently active thread.
destroy()A destructive way to destroy the thread.
dumpStack()Dumps the stack trace for the thread.
enumerate(Thread [])Copies the currently active threads of the group the thread is part of to the array.
getName()Returns the name of the thread.
getPriority()Returns the current priority of the thread.
getThreadGroup()Returns the ThreadGroup instance associated with the thread.
interrupt()Sends an interrupt to the thread.
interrupted()Returns if the thread has been interrupted.
isAlive()Returns if the thread is active (that is, has not been stopped).
isDaemon()Returns if the thread has the Daemon flag set.
isInterrupted()Returns if the thread has been interrupted.
join()Waits for the termination of the thread.
join(long)Waits for the termination of the thread, but waits only for the specified number of milliseconds.
join(long, int) Waits for the termination of the thread, but waits only for the specified number of milliseconds plus an additional number of nanoseconds.
setDaemon(boolean)Marks the thread as a Daemon thread. When there are only Daemon threads running, Java will exit.
setName(String)Sets the name of the thread.
setPriority(int)Attempts to set the priority of the thread, making sure permissions are correct and that the ThreadGroup allows it.
sleep(long)Causes the current thread to sleep for the specified number of milliseconds.
sleep(long, int)Causes the current thread to sleep for the specified number of milliseconds plus an additional number of nanoseconds.
toString()Returns a string representation of the thread.
yield()Yields execution to other waiting threads.

A Runnable Object Example

Figure 12.1 shows EX12A, an example of using the Runnable implementation.


This example uses a thread to count from 10 down to 0 at one-second increments, resets the count, and repeats this cycle. Listing 12.1 shows the code for EX12A.


Listing 12.1. Listing of EX12A.
import java.applet.*;
import java.awt.*;

public class EX12A extends Applet implements Runnable
{
    protected Thread CounterThread;
    protected Label CountLabel;
    protected int count = 10;

    public void init()
    {
        // provide something to look at
        add(new Label("Count:"));
        CountLabel = new Label(Integer.toString(count));
        add(CountLabel);
    }

    public void start()
    {
        if (CounterThread == null) {
            // allocate the thread, using the local run method
            CounterThread = new Thread(this);
            CounterThread.start();
        }
    }

    public void stop()
    {
        if (CounterThread != null) {
            // stop the thread
            CounterThread.stop();
            CounterThread = null;
        }
    }

    public void run()
    {
        while (true) {
           try {
               Thread.sleep(1000);
           }
           catch (InterruptedException e) {}

           CountDown();
        }
    }

    protected void CountDown()
    {
        count--;

        CountLabel.setText(Integer.toString(count));

        if (count == 0)
            // reset to 11 since the first call will move it back
            //   down to 10 and then display
            count = 11;
    }
}

In this example, the init method adds a label that acts as a prompt and a label that will hold the decrementing count.

The Applet.start method, which is called by the browser to tell the applet to start executing, checks to see if a thread has been allocated. If it hasn't, the thread is allocated, passing the applet instance in the constructor so that the local run method will be called and started.

Once the thread is started, the run method will be called. The run method consists of an infinite loop. The first step inside the loop is a little nap. The Thread.sleep method stops execution of the thread for the specified number of milliseconds. Passing a value of 1000 thus produces a delay of one second. During this time, the thread is simply stopped. Note that because the sleep method has the potential of causing a deadlock situation, because no locks are released for the thread while sleeping, this function has the potential of throwing InterruptedException. The next step inside the run method is to call CountDown().

CountDown() decrements the applet's count variable, displays the count in the label, and resets the count if necessary.

Lastly, the stop method takes care of stopping the thread. A check is done to make sure that the thread was allocated. If successful, the thread is stopped and the thread set back to null. Setting of the thread to null accomplishes two things: It allows the garbage collector to free the unused memory and it allows the start method to start another thread the next time the page is activated.

NOTE
Java actually guarantees that the start and stop methods of Applet will be called in the proper order. Therefore the checking of the existence of the thread in EX12A is unnecessary, but is included to show code completeness

Suspend and Resume

Another option, instead of having the Thread instances come and go, is to use the suspend and resume methods of the Thread class:

public final void suspend()
public final void resume()

These methods provide the same effect as starting and stopping the thread, but do not incur the overhead of creating and destroying the Thread instance. Example EX12B extends the EX12A example by placing two buttons on the applet that gives the user the ability to suspend the countdown and then resume it. Figure 12.2 shows the new example.

Figure 12.1 : EX12A.

Only two methods were modified to create EX12B. Listing 12.2 shows the init method of the applet.


Listing 12.2. init method of EX12B.
public void init()
{
    // gives us some control over where the controls are
    //   to be placed
    setLayout(new BorderLayout());

    // provide some control over the count
    Panel p = new Panel();
    p.add(SuspendButton);
    p.add(ResumeButton);
    add("North", p);

    // provide a visual of the count
    p = new Panel();
    p.add(new Label("Count:"));
    CountLabel = new Label(Integer.toString(count));
    p.add(CountLabel);
    add("Center", p);
}

This method now uses a BorderLayout instance so that the buttons and labels will be located next to each other. To support this, the buttons and labels were each added to a panel, which is then added to the applet. For more information about layout managers, see Chapter 5 "Java's User Interface Components."

Listing 12.3 shows the action method of EX12B, which shows what occurs when the buttons are pressed.


Listing 12.3. action method of EX12B.
public boolean action(Event evt, Object arg)
{
    boolean retval = false;             // assume event not processed

    if (evt.target == SuspendButton) {
        if (CounterThread != null)
            CounterThread.suspend();

        retval = true;
    }
    else if (evt.target == ResumeButton) {
        if (CounterThread != null)
            CounterThread.resume();

        retval = true;
    }

    return retval;
}

If the suspend button is pressed and the thread exists, the suspend method is called. This stops the execution of the run method and thus the countdown. If the resume button is then pressed, resume is called for the thread and execution will continue.

CAUTION
Be careful and courteous when using the suspend and resume methods of the Thread class instead of allocating and destroying a Thread instance. If you only suspend a thread when leaving a Web page, the thread is still in existence, just not being used. And, in fact, you may never get back to the page with the suspended thread. This is essentially a memory leak that takes processing capabilities away from subsequent operations

Synchronization

So far, problems with accessing the data in the various threads have not been discussed. And, in fact, the examples have not been thread-safe. Every function that has been called has made the modifications to the variables as needed, without regard to the possibility that another thread may be in the process of changing the values too. To solve this problem, Java has built into the language synchronization support.

Java's Synchronization

Synchronization is the capability of a thread to provide a locking mechanism on an object so that a critical section of code can be executed without the fear of being interrupted, or interleaved with another thread making modifications to the same data. Every object in Java has a built-in lock. This is used by the synchronized keyword to provide access to code by only a single thread.

synchronized can be used in two different ways. The first is around a block of code. The syntax of a synchronized block takes the form

synchronized(object_reference) {
    code_block
}

With this, you can use the locking mechanism for the object referenced to monitor when the code block is executed. For example, consider the CountDown() method of EX12A discussed earlier in this chapter. To make this thread-safe, a synchronized block could be added, as in Listing 12.4.


Listing 12.4. The CountDown() method from EX12A with a synchronized block.
protected void CountDown()
{
    // use the lock in the EX12A class to control access
    synchronized(this) {
        count--;

        CountLabel.setText(Integer.toString(count));

        if (count == 0)
            // reset to 11 since the first call will move it back
            //   down to 10 and then display
            count = 11;
    }
}

The second form of synchronization is applied to entire methods. This is accomplished by placing the synchronized modifier in the method declaration. Listing 12.5 shows the same method as a synchronized method.


Listing 12.5. A synchronized CountDown() method from EX12A.
protected synchronized void CountDown()
{
    count--;
z
    CountLabel.setText(Integer.toString(count));

    if (count == 0)
        // reset to 11 since the first call will move it back
        //   down to 10 and then display
        count = 11;
}

This method of synchronization is probably more common due to its ease of readability.

Synchronization Example

To make use of the synchronization method, the working example will be extended so that the maximum value of the countdown can be modified by the user. Figure 12.3 shows the interface to example EX12C.

Figure 12.2 : EX12B.

To accomplish the transition to a thread-safe applet, only a couple of modifications had to be made to the code of EX12B to produce EX12C.

A new control, MaxCountTextField, was added to the applet to collect the new starting point for the count. This field was added, along with the prompt for the field in the init method:

protected TextField MaxCountTextField;
protected int maxCount = 10;
protected int count = maxCount;

public void init()
{

    ...

    // let the user set the max count
    p = new Panel();
    p.add(new Label("Enter the starting point:"));
    MaxCountTextField = new TextField(Integer.toString(maxCount), 3);
    p.add(MaxCountTextField);
    add("South", p);
}

A new else clause was added to the action method to handle when the user pressed Return after entering a new maximum value. This handler calls SetMaxCount to read that information and reset the count. These methods are shown in Listing 12.6.


Listing 12.6. action and SetMaxCount methods of EX12C.
public boolean action(Event evt, Object arg)
    {
        boolean retval = false;         // assume event not processed

        if (evt.target == SuspendButton) {
            if (CounterThread != null)
                CounterThread.suspend();

             retval = true;
        }
        else if (evt.target == ResumeButton) {
            if (CounterThread != null)
                CounterThread.resume();

            retval = true;
        }
        else if (evt.target == MaxCountTextField) {
            SetMaxCount();

            retval = true;
        }

        return retval;
    }

    protected synchronized void SetMaxCount()
    {
        try {
            maxCount = Integer.parseInt(MaxCountTextField.getText());

            count = maxCount + 1;
        }
        catch (NumberFormatException e)
        {
            // reset the contents back to the current max
            MaxCountTextField.setText(Integer.toString(maxCount));
        }
    }

Because both the countdown thread of the applet and the user portion of the applet modify the contents of the count variable, it is very important that the program blocks simultaneous modifications of count. Therefore CountDown is now a synchronized method:

protected synchronized void CountDown()
    {
        count--;

        CountLabel.setText(Integer.toString(count));

        if (count == 0)
            // reset to maxCount + 1 since the first call will 
            //   move it back down to maxCount and then display
            count = maxCount + 1;
    }

In addition, since the SetMaxCount resets the current count, it is also a synchronized method, as shown in Listing 12.6.

Wait and Notify

Synchronization provides a mechanism to lock execution of a single thread through a critical section. However, there are times when a thread is in a critical section but control needs to be given up for other threads to process, even though the critical section has not been completed. This can be accomplished with the wait and notify methods. These methods are not in the Thread class, as you might suspect. Instead, they are in the Object class. This is because they deal directly with the locking mechanism, which also happens to be in the Object class.

There are three versions of the wait method:

public final void wait()
public final void wait(long)
public final void wait(long, int)

The first version waits until the thread is interrupted or until a notify method is called for the object. The next version is similar to the first in that it waits, but will be automatically notified if the specified time-out elapses before the thread is notified. The last version is very similar to the second, except that the time-out period can be specified in finer increments. These functions must be called in a synchronized block or method that has previously obtained a lock on the object. If that is not the case, an exception of the type IllegalMonitorStateException will be thrown. Note that all three of these functions release the lock on the object before going to sleep. Therefore, if the thread is notified, the lock must be re-obtained on the object before the thread continues.

To wake a waiting object, one of the two notify methods should be called:

public final void notify()
public final void notifyAll()

notify wakes up a single thread that is waiting on an object. However, if there is more than a single thread waiting on an object, there is no guarantee that the first object that called wait will be the first object notified. In cases where there are more than a single thread waiting on an object, notifyAll can be used to wake up all of the threads waiting. Once a thread is awakened, processing resumes at the point immediately after the point where the wait method was called.

CAUTION
When using the wait and notify methods, make sure to take into account that the object locks are released while the thread is waiting. Therefore, another thread can easily modify class variables before the thread is notified. Consider the following simple example
class BadAssumption
{
int n;
public synchronized void DoSomething()
{
n = 5;
// do other processing here
wait();
// do other processing here
// allocate 5 strings
Strings [] myStrings = new Strings[n];
}
}
Even though the DoSomething is a synchronized method, there is no guarantee that n will still contain a value of 5 when myStrings is allocated.

ThreadGroups

Threads allow programs to be separated into processes. To add a little control to the creation of processes, Java provides the ability to group threads. This functionality is contained in the ThreadGroup class. In addition to the basic grouping ability, the ThreadGroup class provides functionality to set priorities and is the basis for the security concepts of Java programming.

Using ThreadGroups

ThreadGroups are used to group related threads together. They also have the ability to group threads with other ThreadGroups. The net result is that hierarchical trees can be generated with ThreadGroups.

There are two constructors for the ThreadGroup class:

public ThreadGroup(String)
public ThreadGroup(ThreadGroup, String)

Because an instance of the ThreadGroup is a node of a tree, every node has a parent ThreadGroup. The first constructor creates a named ThreadGroup, whose parent is the ThreadGroup of the currently executing thread. The second constructor creates a named ThreadGroup, setting the parent to be the passed-in ThreadGroup.

To create a thread in a ThreadGroup, one of the Thread constructors shown in Table 12.4 must be used.

Table 12.4. Public constructors of the Thread class used to place a thread into a ThreadGroup.

MemberPurpose
Thread(ThreadGroup, Runnable) Creates a thread in the ThreadGroup, with a default name, that will call the Runnable object's run method.
Thread(ThreadGroup, Runnable, String) Creates a named thread in the ThreadGroup that will call the Runnable object's run method.
Thread(ThreadGroup, String)Creates a named thread in the ThreadGroup.

One advantage of using a ThreadGroup is that there are several functions that can be called for the ThreadGroup that are recursively applied to all threads contained within the ThreadGroup. Table 12.5 contains a summary of these methods.

Table 12.5. Public methods of ThreadGroup that are applied recursively to contained threads.

MemberPurpose
resume()Resumes all processes in the ThreadGroup.
Stop()Stops all processes in the ThreadGroup.
Suspend()Suspends all processes in the ThreadGroup.

Table 12.6 contains other public methods of the ThreadGroup class.

Table 12.6. Additional public methods of the ThreadGroup class.

MemberPurpose
activeCount()Returns the number of active threads in the current ThreadGroup and all offspring.
ActiveGroupCount()Returns the number of active ThreadGroups in the current ThreadGroup and all offspring.
CheckAccess()Determines whether the current thread has permissions to modify this ThreadGroup.
Enumerate(Thread [])Returns a list of the active threads in the ThreadGroup.
Enumerate(Thread [], boolean) Returns a list of the active threads in the ThreadGroup; optionally traverses ThreadGroup tree recursively.
Enumerate(ThreadGroup [])Returns a list of active groups.
Enumerate(ThreadGroup [], Returns a list of active groups; optionally traverses
boolean)ThreadGroup tree recursively.
GetMaxPriority()Returns the highest priority in group.
GetName()Returns the name.
GetParent()Returns parent ThreadGroup.
IsDaemon()Returns Daemon flag.
ParentOf(ThreadGroup)Returns if the current ThreadGroup is an ancestor of the argument.
SetDaemon(boolean)Sets the Daemon flag.
SetMaxPriority()Sets the highest possible priority this ThreadGroup can have.
ToString()Returns a string representation of this ThreadGroup.
UncaughtException(Thread, Throwable) Called by Java Virtual Machine when an exception does not get caught by a contained thread.

Priority

Every thread can have an associated priority. This priority determines how often the thread gets serviced. To set the priority, the following method in Thread is used:

public void setPriority(int)

The parameter of this function can be one of the following three values defined in Thread:

MAX_PRIORITY
NORM_PRIORITY
MIN_PRIORITY

In general, threads within ThreadGroups will all have the same priority, since similar tasks will normally have the same priority. The following methods in ThreadGroup support this functionality:

public int getMaxPriority()
public void setMaxPriority(int)

ThreadGroups Example

Figure 12.4 shows a sample program using ThreadGroups.

Figure 12.3 : EX12C.

This example uses two ThreadGroups; each of them has a number of threads drawing a polygon on the applet. Additional support is provided to start and stop the drawing via buttons on the applet. Listing 12.6 shows the DrawThread class used in example EX12D.


Listing 12.6. DrawThread class of EX12D.
class DrawThread extends Thread
{
    protected static Graphics g;
    protected static Random r = new Random();
    protected int totalWidth, totalHeight;
    protected int count;

    public static void SetGraphics(Graphics _g)
    {
        g = _g;
    }

    public DrawThread(ThreadGroup group, int totalWidth, 
            int totalHeight, int count)
    {
        super(group, Integer.toString(count));

        this.totalWidth = totalWidth;
        this.totalHeight = totalHeight;
        this.count = count;
    }

    public void run()
    {
        while (true) {
            try {
                sleep(GetNextRandom(4, 1) * 1000);
            }
            catch (InterruptedException e) {}

            if (g != null)
                Draw(g, GetNextRandom(totalWidth, 0), 
                        GetNextRandom(totalHeight, 0));
        }
    }

    protected synchronized int GetNextRandom(int range, int offset)
    {
        return (int)(r.nextDouble() * (double)range) + offset;
    }

    protected void Draw(Graphics g, int x, int y)
    {
    }
}

DrawThread is the superclass for two classes: DrawCircleThread and DrawSquareThread. The classes do what their names imply.

The base class takes a ThreadGroup in the constructor. This ThreadGroup is used to hold common threads drawing similar polygons. In addition, the constructor takes some configuration parameters for where the polygon can be drawn. run is overridden in the Thread class and contains an infinite loop. Inside the loop, the thread first goes to sleep for 1 to 5 seconds. Notice that all threads use the same random number generator. Therefore, GetNextRandom is a synchronized method. The last step in the run method is to call Draw to draw a polygon at a random location on the applet.

The DrawCircleThread and DrawSquareThread classes exist only to draw their respective polygons. Notice again that since the Graphics instance is being changed by the Draw methods, these methods must be synchronized:

class DrawCircleThread extends DrawThread
{
    protected final static int graphicsSize = 30;

    public DrawCircleThread(ThreadGroup group, int totalWidth, 
            int totalHeight, int count)
    {
        super(group, totalWidth - graphicsSize, 
                totalHeight - graphicsSize, count);
    }

    protected synchronized void Draw(Graphics g, int x, int y)
    {
        g.setColor(Color.red);
        g.fillOval(x, y, graphicsSize / count, graphicsSize / count);
    }
}

class DrawSquareThread extends DrawThread
{
    protected final static int graphicsSize = 5;

    public DrawSquareThread(ThreadGroup group, int totalWidth, 
            int totalHeight, int count)
    {
        super(group, totalWidth - graphicsSize, 
                totalHeight - graphicsSize, count);
    }

    protected synchronized void Draw(Graphics g, int x, int y)
    {
        g.setColor(Color.green);
        g.fillRect(x, y, graphicsSize * count, graphicsSize * count);
    }
}

Listing 12.7 shows the applet using the above threads.


Listing 12.7. Applet class of EX12D.
import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX12D extends Applet
{
    protected Panel p = new Panel();
    protected Button CircleButton = new Button("Stop Circles");
    protected Button SquareButton = new Button("Stop Squares");
    protected ThreadGroup Circles = new ThreadGroup("Circles");
    protected ThreadGroup Squares = new ThreadGroup("Squares");

    public void init()
    {
        setLayout(new BorderLayout());

        p.add(CircleButton);
        p.add(SquareButton);
        add("South", p);
    }

    public boolean action(Event evt, Object obj) 
    {
        boolean result = false;         // asume no action

        if ("Start Circles".equals(obj)) {
            Circles.resume();
            CircleButton.setLabel("Stop Circles");

            result = true;
        }
        else if ("Stop Circles".equals(obj)) {
            Circles.suspend();
            CircleButton.setLabel("Start Circles");

            result = true;
        }
        else if ("Start Squares".equals(obj)) {
            Squares.resume();
            SquareButton.setLabel("Stop Squares");

            result = true;
        }
        else if ("Stop Squares".equals(obj)) {
            Squares.suspend();
            SquareButton.setLabel("Start Squares");

            result = true;
        }

        return result;
    }

    public void start()
    {
        DrawThread.SetGraphics(getGraphics());

        // add two circle threads
        for (int count = 1; count <= 2; count++)
            (new DrawCircleThread(Circles, size().width, 
                    size().height - p.size().height, 
                    count)).start();

        // add three square threads
        for (int count = 1; count <= 3; count++)
            (new DrawSquareThread(Squares, size().width, 
                    size().height - p.size().height, 
                    count)).start();
    }

    public void stop()
    {
        Circles.stop();
        Squares.stop();
    }
}

The init method of the applet places the components needed onto the applet. action takes care of processing the button clicks. ThreadGroup.suspend and ThreadGroup.resume are used to start and stop the display of all of the same types of polygons at once.

start allocates all of the threads being used in the applet, adding them to the appropriate ThreadGroups. stop simply calls the ThreadGroup function to stop all threads.

Summary

In this chapter, threads were explored. Threads can be created by either implementing the Runnable interface or by deriving a class from the Thread class. Once the threads are created, synchronization issues need to be addressed. This is accomplished by using synchronized blocks or synchronized methods around critical sections that modify class variables. ThreadGroups can then be used to logically group threads together to form logical groupings and later will be used to create hierarchical trees that help with Java security.