Multithreading

Visual J Plus Plus provides another set of capabilities through the com.ms.wfc.app package together with the standard Java classes: multithreading. This section will demonstrate different ways of implementing multithreading and provide some guidelines for when to use each approach. In addition, I will touch on some of the dangers of multithreading. Let me begin by explaining the attraction of multithreading.

What is Multithreading?

There are many times when you want your program to do more than one thing at the same time. You saw a hint of that in the previous TimerDemo app. While the clock was being updated in the upper box, the user was able to turn the stopwatch on and off. Even though both controls are tightly bound by the working of the handleClockTick() method, to the user the two windows appear to function independently.

To understand multithreading, you should first understand why TimerDemo works this way. It's critical that the event handler, handleClockTick(), perform its task and return control to WFC quickly. During the time handleClockTick() is running, the TimerDemo program is unresponsive to all other input—that is, if the user clicks one of the buttons during the time that the program is in the middle of the handleClockEvent() method, the click event is queued up. This event isn't processed until handleClockEvent() completes and returns control to the program.

NOTE
Of course, all events originate from Windows messages that are queued up and handled by the event loop. When the timer expires, for example, it generates a WM_TIMER message that is queued up with any other messages for processing. Once the timer message bubbles to the top of the queue, WFC creates the timer event and passes it to the handleClockEvent() method for processing.

As long as handleClockEvent() doesn't take too long, the user won't perceive any delay between clicking the mouse button and the program responding. The user would suddenly become aware of unresponsiveness on the part of the program, however, if handleClockEvent() were to go off and perform some extended processing such as read a file or perform an extended calculation. The user will think that the program has crashed if the delay is long enough. It's important that an event handler not perform any function that will take very long.

Another way that TimerDemo performs its magic is by placing both the clock and timer capabilities in the single handleClockEvent() method. This coding practice is justifiable in simple cases, especially when two features are closely related. When the features are unrelated, however—a word processor might have methods to perform text entry, spell checking, and reformatting of text—placing them into a single method is both difficult and error prone.

So what do you do if a user action results in a request for a feature that takes a long time to perform? The best tactic is to branch off to a new thread of execution. This new thread appears to execute parallel to the original thread. The original thread can return to Visual J Plus Plus to continue processing event requests while the new thread performs the lengthy operation. These secondary threads are commonly called background threads, because they continue to execute behind the user interface thread.

NOTE
Several background threads already exist in your Visual J++ app without your creating any. One is the garbage collection thread, which searches for objects that are no longer in use and returns them to the memory pool.

How does multithreading work?

A thread that has control of the CPU is said to be in the RUNNING state. Realize that multiple threads only appear to work in parallel. Since there is only one CPU, in reality only a single thread can execute at any given time. Therefore, only one thread can be in the RUNNING state.

NOTE
Machines running under Microsoft Windows NT can have more than one CPU. Because each CPU can run only a single thread, there can be as many threads in the RUNNING state as there are CPUs in the machine.

A thread waiting for something to happen, like for a file read request to complete, is said to be BLOCKED.

NOTE
The thread that reads the Windows message queue is in effect BLOCKED on the message queue.

A thread that is neither BLOCKED nor RUNNING is said to be READY.

In Visual J Plus Plus, each thread is assigned a priority ranging from 9 (high priority) to 1 (low priority). A thread in the RUNNING state retains control of the CPU until one of three things happens:

As an example, Figure 10-3 shows three threads running concurrently on one CPU. Each thread is allowed a certain amount of time to run. When time is up for the running thread, any thread with the same priority runs until its time slice is over. You can see in Figure 10-3 that Thread 1 runs until its time is up, and then control moves to Thread 2 because Thread 2 has the same priority as Thread 1. This process continues in a loop until either Thread 1 or Thread 2 is BLOCKED. If Thread 1 is BLOCKED, Thread 2 runs continuously until Thread 1 is unBLOCKED and Thread 2's time runs out.

Java Click to view at full size.

Screenshot-3. An example of two high-priority threads and one lower-priority thread sharing the CPU.

If Threads 1 and 2 are both BLOCKED, the lower-priority Thread 3 finally gets its chance to run. Thread 3 stops running and returns to the READY state as soon as a higher-priority thread is unBLOCKED.

When a thread loses control of the CPU—meaning it changes from the state RUNNING to any other state—it's said to be suspended.

Each thread has associated with it a data structure that can hold all the information needed to store the state of a thread. (This information consists mostly of the contents of the CPU registers.) When the thread is suspended, this data structure stores the CPU data. When the thread next gains control of the CPU, this data is restored and the thread continues executing from where it left off the last time it lost control. The thread is completely unaware of the break in execution.

What multithreading is not

Multithreading isn't a way to gain extra performance from your CPU. If a given process is slow, breaking it into multiple threads won't make it faster. It will actually make the process slower, because using multiple threads adds the extra overhead of switching control from one thread to another. Multithreading is a way to do these things:

The second item is of course a special case of the first item.

dangers of multithreading

Multithreading does carry with it certain dangers. Consider the following unlikely but possible banking example.

Suppose I have a balance in my checking account of $1000 (this is a purely fictional example, I assure you). I am at an ATM with my bank card in hand, making my usual $40 quick cash withdrawal. I put in my card, type in the password, and choose the quick cash button. The bank computer registers my request, reads my balance, and subtracts $40. Before the program can store the results back into my account, however, the thread handling my transaction is suspended.

Unbeknownst to me, my wife is at this moment in the bank making a deposit of $1000 to the checking account. A separate bank computer program now gets control from my suspended thread. It reads my bank balance and adds the deposit, leaving a bank balance of $2000.

What happens next will leave me very happy and the bank distressed, or will leave me quite distressed and the bank happy, depending on the vagaries of thread timing. If my wife's program continues, my bank balance will be updated to $2000 in the bank's database. Once her program terminates or is suspended, my ATM thread will continue and store the post-withdrawal result of $960 into the database, thereby wiping out any record of my wife's deposit. (Unless I can find her deposit slip, which is very unlikely, I am very unhappy.)

If, on the other hand, my wife's program is somehow suspended—perhaps because it has used up its time slice—and my ATM thread gets control, then my balance of $960 is written to the bank's database only to be overwritten with the $2000 balance calculated by my wife's program. (The bank is now very unhappy, and I'm delirious.)

Such so-called race conditions or thread collisions are common occurrences in multithreaded programs. An area where a race condition can occur is called a critical section. In our example, the critical section in both the withdrawal and deposit programs begins when the program reads the balance from the accounts database and ends when the program completes writing the balance back.

NOTE
For a race condition to occur, all that has to happen is for more than one thread to update a resource such as a memory location. If only one thread writes to the memory location—even if multiple threads read from it—there is no race condition.

How Java handles race conditions

The Java language contains built-in support to avoid race conditions. Marking a method as synchronized means that only one thread can execute the method at a time. If thread A is suspended while in the middle of executing a synchronized method and thread B attempts to invoke that method, thread B will instantly become BLOCKED until thread A can regain control and exit from the method.

If the critical section involves only a small part of the method, say the updating of a data member, you might mark a block of code in the method as synchronized in the following way:

int pay;
void calculateRaise(float evaluation)
{
 // use some method to calculate the raise
 // based on the evaluation
 .
 .
 . // entering a critical section; synchronize it to
 // avoid a collision; obj is some object
 synchronized (obj)
 {
 pay = pay * raise;
 }
 // method continues unsynchronized
}


HOW DOES JAVA SYNCHRONIZE METHODS?


Visual J Plus Plus implements the synchronization of methods through a mutex. The mutex is checked and set upon entry into the method and cleared upon exit. Java maintains one mutex per object and another for the entire class. Method objects use the object mutex, and static methods use the class mutex.

Suppose that obj1 and obj2 are objects of the same class and m() is a non-static synchronized method in that class. When thread A makes the call obj1.m(), thread A grabs the object mutex before entering m(). When thread B calls obj1.m() (or any other nonstatic synchronized method using the same object obj1), it's BLOCKED as it attempts to grab the object's mutex. Since object obj2 has it's own mutex, it's perfectly okay for thread A to call obj1.m() while thread B calls obj2.m(). If m() were a static method, only one thread would be able to call it, no matter which object it's calling from.

Mutexes are counters: it's okay for thread A to call one synchronized method from within another synchronized method. Synchronized methods are also recursive. Each time a synchronized method is invoked, the mutex is incremented. Upon each return, the mutex is decremented. The mutex isn't unBLOCKED until it returns to 0.

If an exception occurs from within a synchronized method, the mutex is decremented automatically as control passes from the method.

Here we can see that only the section updating the pay variable is synchronized. The mutex of the object obj is used to implement the synchronization. (A mutex—or mutual exclusion flag—is a special flag that allows one thread mutually exclusive access to critical areas.) In general, the object this can be used as the synchronizing object as long as this doesn't refer to the Thread object (since each thread has its own Thread object).

Using the Thread class

The easiest way to create a new thread is to create a class that extends the class Thread. To start the new thread, the parent thread must first create an object of the new class and then invoke the start() method. The start() method causes execution to begin with the method run().

/**
 * Start a new thread.
 */
 void parentMethod()
 {
 Thread thread = new MyThread();
 thread.start();
 }
 /**
 * Implement a new thread of execution.
 */
 class MyThread extends Thread
 {
 // execution starts here as soon as
 // the parent thread calls start()
 public void run()
 {
 }
 }


Extending the class Thread means that MyThread can't extend some other class. To avoid this unfortunate side effect, the Java standard library defines a second mechanism for creating new threads. Any class that implements the interface Runnable can have a thread wrapped around it. The Runnable interface has only one member—the method public void run(). In practice, this thread creation mechanism appears as follows:

/**
 * Start a new thread.
 */
 void parentMethod()
 {
 // wrap my class in a thread
 Thread thread = new Thread(new MyClass());
 // now start the thread
 thread.start();
 }
 /**
 * Implement a class that can be wrapped in a thread.
 */
 class MyClass extends MyBaseClass implements Runnable
 {
 // execution starts here as soon as
 // the parent thread calls start()
 public void run()
 {
 }
 }


The following demonstration program uses the Java Thread class to implement a common multithreading problem.

MultiThread1 app

The following MultiThread1 demonstration app continuously calculates prime numbers while remaining responsive to user input. Just to prove the point, this app calculates two sets of prime numbers "simultaneously."

Forms Designer work

The Forms Designer work for MultiThread1 consists primarily of creating two sets of matching output Edit controls, each with a label, a start button, and a stop button. (See Figure 10-4.) Start the forms work with the first Edit control. Set the name to outputEdit1. Clear the text property value box. Now anchor the Edit control to the left, top, and right edges of the form. Add a Label control above the Edit control, and set the label text to Last Prime Number Calculated (#). The program will replace the pound sign with the priority of the background task. Anchor the label to the left, top, and right edges of the form. Below the outputEdit1 control, add two buttons: one labeled Start and one labeled Stop. Anchor the Start button to the top and left, and anchor the Stop button to the top and right edges of the form. Double-click each button to create the click event handlers.

Now repeat the process for a second set of controls immediately below the first.

Background.java code

The code for MultiThread1 consists of two .java files, the usual Form1.java and also the file Background.java. (Select Add Class from the Project menu to add Background.java.) The Background.java file appears below.

import com.ms.wfc.ui.*;
/**
 * Thread that calculates primes and reports
 * them to output control.
 */
public class Background extends Thread
{
 // number of primes
 int numberOfPrimes = 0;
 // output control
 Control output;
 // stopped == true -> thread stopped
 boolean stopped;
 /**
 * Create a background process in the stopped state.
 */
 public Background(Control output)
 {
 this.output = output;
 stopped = true;
 }
 /**
 * Test next number for prime.
 */
 boolean isPrime(long number)
 {
 // test all the multiples from 2 to the
 // square root of the number (increment
 // to handle potential round-off problems)
 long max = (long)Math.sqrt(number)+ 1;
 for (long test = 2; test <= max; test++)
 {
 // if number is evenly divisible by test…
 long remainder = number % test;
 if (remainder == 0)
 {
 // then it can't be prime
 return false;
 }
 }
 // made it through; it must be prime
 return true;
 }
 /**
 * Start the Background thread.
 */
 public void run()
 {
 try
 {
 // keep calculating prime numbers
 // until we wrap around
 for (long i = 0; i >= 0; i++)
 {
 // give up control
 Thread.yield();
 // while stopped, keep giving up control
 while (stopped)
 {
 Thread.sleep(100);
 }
 // now calculate a new prime
 if (isPrime(i))
 {
 // count it
 numberOfPrimes++;
 // output prime number to control
 // specified
 output.setText("#" +
 toString(numberOfPrimes) +
 "- " +
 toString(i));
 }
 }
 }
 catch(Exception e)
 {
 output.setText(e.getMessage());
 }
 }
 /**
 * Convert a number into a string with commas.
 */
 private String toString(long value)
 {
 // convert the long into a string
 String s = Long.toString(value);
 // now add commas to that string
 StringBuffer sb = new StringBuffer(20);
 int offset = 0;
 for(int length = s.length();
 length > 0;
 length--)
 {
 // every 3 digits from the right side…
 if ((length % 3) == 0)
 {
 // but not at the beginning…
 if (offset != 0)
 {
 // put a comma
 sb.append(',');
 }
 }
 // now add another digit
 sb.append(s.charAt(offset++));
 }
 return sb.toString();
 }
 /**
 * Activate the background process.
 */
 public void activate(boolean active)
 {
 stopped = !active;
 }
}


The class Background extends the class Thread. The constructor for Background takes one argument, the WFC control to which Background will output its prime numbers as it calculates them. The program sets one other variable, stopped, to true. This flag will be used later to signal Background to stop calculating primes.

The parent thread creates an object of class Background and then calls the object's start() method. (See the Form1 code that follows.) As soon as it does, Visual J Plus Plus creates a new thread. After some setup, this new thread will call Background.run(). The Background.run() method sits in an essentially infinite loop. (The loop increments a long variable and terminates when the variable contains a negative number).

The new thread immediately invokes the rescheduler by calling Thread.yield(). If this is the only thread that is ready to run, it will instantly get control back.

Once it has given another thread a chance, run() then enters another loop. As long as stopped is true, run() continues to call Thread.sleep() in a loop. (The stopped variable gets changed in the Background.active() method, explained below.) The argument 100 indicates that the thread should block for 100 milliseconds.

Once stopped is false, run() continues by calling isPrime(i) to check whether i (the counter in the for loop) is prime or not. If it is, run() increments the prime number count and outputs both the prime number count and the prime number to the output WFC control that was passed to the Background() constructor. The method Background.toString() does nothing more than insert commas every three digits from the end to increase the readability of the resulting number.

The Background.isPrime() method uses a different algorithm than does the Sieve of Eratosthenes benchmark presented in Chapter 1. This version checks the candidate number by calculating the modulo of number with respect to each whole number from 2 up through the square root of number. If the modulo is zero, number is evenly divisible by that number and therefore can't be a prime number. If isPrime() makes it all the way through the loop without finding an even denominator, number must be prime and isPrime() returns true.

The Background method activate(boolean) sets the stopped variable to true or false. (The activate() method is called from the parent program, in this case Form1.) By referring to run(), you can see that setting this variable in effect turns the Background thread off and on.

Form1.java code

Most of the real work for the MultiThread1 app is done in the Background class; the Form1 class is straightforward:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * This class demonstrates background processing using threads.
 */
public class Form1 extends Form
{
 // define two background threads
 Background bg1;
 Background bg2;
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // create a thread of normal priority
 bg1 = new Background(outputEdit1);
 bg1.setPriority(Thread.NORM_PRIORITY);
 // update the label to reflect the priority
 setLabel(bg1, label1);
 // repeat for a second thread
 bg2 = new Background(outputEdit2);
 bg2.setPriority(Thread.NORM_PRIORITY - 1);
 setLabel(bg2, label2);
 // start both processes (note that both
 // start in the inactive state)
 bg1.start();
 bg2.start();
 }
 /**
 * Update the specified label with the priority of thread.
 */
 private void setLabel(Thread thread, Label label)
 {
 int priority = thread.getPriority();
 String priString = Integer.toString(priority);
 char priChar = priString.charAt(0);
 label.setText(label.getText().replace('#', priChar));
 }
 .
 .
 .
 private void button1_click(Object source, Event e)
 {
 bg1.activate(true);
 }
 private void button2_click(Object source, Event e)
 {
 bg1.activate(false);
 }
 private void button3_click(Object source, Event e)
 {
 bg2.activate(true);
 }
 private void button4_click(Object source, Event e)
 {
 bg2.activate(false);
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit outputEdit1 = new Edit();
 Label label1 = new Label();
 Button button1 = new Button();
 Button button2 = new Button();
 Edit outputEdit2 = new Edit();
 Label label2 = new Label();
 Button button3 = new Button();
 Button button4 = new Button();
 private void initForm()
 {
 // …created by the Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


Once control returns from the initForm() method, the Form1 constructor creates a Background object bg1 and passes it the outputEdit1 object for output. The constructor then sets the priority to the normal priority for a Visual J Plus Plus thread. Form1() then updates the label1 Label control text with this priority. The setLabel() method replaces the # character in the label with an ANSI representative of the priority.

NOTE
Visual J Plus Plus thread priorities range from 1 to 9, with 5 being a normal priority.

The Form1() constructor repeats this process to create a second Background object called bg2 and to attach it to the outputEdit2 Edit control. The Form1() constructor sets the priority of the bg2 thread to be one less than the priority of bg1. Next Form1() calls bg1.start() and bg2.start() to start the two Background threads running on their own.

The button_click() methods simply call Background.activate() and pass true or false to bg1 or bg2, depending on which thread the button is trying to stop or start.

Executing MultiThread1 generates output as shown in Figure 10-4. What doesn't come across in this static screen capture is the dynamic aspect of the app. With both threads enabled, the upper box updates so rapidly that the user can't possibly read individual values, and the lower box updates slowly if at all. While the upper box is updating, however, the user can resize and move the form window without much apparent effect. (The main window repaints slightly slower; you can see that rapidly moving the window generates window trails similar to mouse trails).

As soon as the user pauses the higher-priority thread, however, the lower- priority thread snaps into gear and updates at the same high rate as the higher- priority thread did previously.

NOTE
When you pause the higher priority thread (bg1), the priority of the lower priority thread (bg2) doesn't change. If you had other threads at the same priority as bg1 that were not BLOCKED, the increase in bg2's performance would not be that pronounced.

Screenshot

Screenshot-4. The two boxes in the MultiThread1 app update rapidly with the calculated prime number, while still allowing the user to perform routine actions like minimizing, maximizing, and moving the window.

Using the WFC createThread() Method

One inconvenience of the approach demonstrated in MultiThread1 is the requirement to create a separate class in order to create a new thread. WFC supports an alternative approach by enabling you to create a new thread around a delegate. (Refer back to for a discussion of delegates.) The following MultiThread2 app demonstrates the use of this strategy. Since this app is a minor variation on MultiThread1, the Forms Designer work is the same.

MultiThread2 code

The code for MultiThread2 is as follows. (Methods duplicated from MultiThread1 are partially or completely removed from this listing; the entire version of MultiThread2 appears on the companion CD-ROM.)

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * This class demonstrates the app.createThread() method.
 */
public class Form1 extends Form
{
 /**
 * Control the flow of the threads we form.
 */
 class FlowControl
 {
 private boolean active = false;
 boolean isActive()
 {
 return active;
 }
 void setActive(boolean active)
 {
 this.active = active;
 }
 }
 // create a flow control for each thread
 // (this is used to start and stop the calculation)
 FlowControl flowControl1 = new FlowControl();
 FlowControl flowControl2 = new FlowControl();
 /**
 * Delegate is used to create background thread.
 * * @param flowControl - starts and stops background thread
 * @param control - control to accept primes
 */
 delegate void Background(FlowControl flowControl,
 Control control);
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // execute delegate in its own thread
 Thread thread1 =
 app.createThread(new Background(this.run),
 new Object[]{flowControl1,
 outputEdit1},
 Thread.NORM_PRIORITY);
 setLabel(thread1, label1);
 // repeat the process for the second thread at
 // a lower priority
 Thread thread2 =
 app.createThread(new Background(this.run),
 new Object[]{flowControl2,
 outputEdit2},
 Thread.NORM_PRIORITY - 1);
 setLabel(thread2, label2);
 }
 /**
 * Increment the prime number counter.
 */
 synchronized void countPrimeNumber()
 {
 numberOfPrimes++;
 }
 /**
 * Start the Background thread.
 */
 public void run(FlowControl flowControl, Control output)
 {
 try
 {
 // keep calculating prime numbers
 // until we wrap around
 for (long i = 0; i >= 0; i++)
 {
 // give up control
 Thread.yield();
 // while not active, keep giving up control
 while (!flowControl.isActive())
 {
 Thread.sleep(100);
 }
 // now calculate a new prime
 if (isPrime(i))
 {
 // count it
 countPrimeNumber();
 // output prime number to control
 // specified
 output.setText("#" +
 toString(numberOfPrimes) +
 "- " +
 toString(i)); }
 }
 }
 catch(Exception e)
 {
 output.setText(e.getMessage());
 }
 }
 private String toString(long value)
 {
 .
 .
 .
 }
 private void button1_click(Object source, Event e)
 {
 flowControl1.setActive(true);
 }
 private void button2_click(Object source, Event e)
 {
 flowControl1.setActive(false);
 }
 private void button3_click(Object source, Event e)
 {
 flowControl2.setActive(true);
 }
 private void button4_click(Object source, Event e)
 {
 flowControl2.setActive(false);
 }
 // …from here the same as MultiThread1…
}


The most important difference between MultiThread2 and its MultiThread1 cousin is the use of the app.createThread() method to create the new threads.

The first argument to createThread() is a delegate object of class Background. The delegate declaration of Background defines two arguments: an object of the inner class FlowControl and an output WFC Control object. These arguments are necessary since there is no thread class constructor to save the values these arguments contain. The Background delegate returns a void as a requirement for being called by createThread(). Any delegate used with createThread() must return a void because createThread() doesn't return to the caller.

In this case, the delegate is created using the method Form1.run(). It is at this method where execution of the new thread begins. The number and type of arguments plus the return type of run() must match the declaration of the delegate Background. If they don't, an error is generated at compile time.

The second argument to createThread() is an array of objects to be passed to run(). Again, the number and type of these arguments must match the declaration of run(), which means they also match the declaration of the delegate Background. If they don't, a run-time exception is generated as soon as the new thread tries to start.

The final argument to createThread() is the priority.

NOTE
When you use createThread(), there is no need to call start(). The thread is created in an active state.

It's necessary to create the inner class FlowControl to contain the active flag. This is because run() needs a reference back to the same active flag that the button_click() methods are accessing. Had the program passed active directly, run() would have simply received the value of the member, either true or false.

Is createThread() worth the trouble?

After comparing the two versions of MultiThread, we are left with the question of whether it's actually worth it to use createThread() to avoid creating a new subclass of Thread (or a new class that implements Runnable). I tend to think no. After all, the delegate Background is a new class.

Although I haven't seen the inner workings of the Thread class, I imagine that it invokes createThread(). Or perhaps Thread and createThread() both access the same Windows API functions to create a Windows thread. Either way, the net effect of the two techniques is the same. Using the Thread class seems the simpler of the two approaches to me.

Synchronizing access to numberOfPrimes

Notice how MultiThread2 allows both threads to use the same variable, numberOfPrimes. I did this as a demonstration rather than because of any overriding requirements of the program.

Since both threads in MultiThread2 update the same numberOfPrimes variable by incrementing it, access to numberOfPrimes must be controlled. The program does this by defining a new synchronized method, countPrimeNumber(), to perform the incrementation. The statement that incremented numberOfPrimes in run() is replaced in this example by a call to countPrimeNumber(). Since countPrimeNumber() is synchronized, the race condition problem is avoided.

Notice that a few statements later, run() accesses numberOfPrimes directly when it passes its value to toString(). This doesn't represent a race condition because the statement doesn't update the value, it only reads it. It's possible for the numberOfPrimes variable to be updated by some other thread between the time that it's incremented and the time that it's read. The following subset of run() uses a synchronized block to avoid this possibility. (Both variations appear in the version of MultiThread2 on the companion CD-ROM, with one commented out):

// now calculate a new prime if (isPrime(i))
{
 // count it
 synchronized(mutex)
 {
 numberOfPrimes++;
 // output prime number to the control
 // specified
 output.setText("#" +
 toString(numberOfPrimes) +
 "- " +
 toString(i));
 }
}


Here the object mutex is defined to be of the empty inner class Mutex as follows:

/**
 * Define a dummy class to use as a mutex.
 */
class Mutex
{
}
Mutex mutex = new Mutex();


Defining a class object mutex provides the block of code with a unique mutex to synchronize on.

NOTE
In this example, I could not use the this object. Each thread is a separate instance of Background. Thus, each thread has a different mutex. Synchronizing on the this object would not keep one thread from interfering with another.

MDIThread app

Although you can create a Multiple Document Interface (MDI) app without creating multiple threads, using multiple threads is common.

NOTE
An MDI interface has several child forms residing within a single parent form. These child forms might refer to different documents—hence the name MDI—but this isn't required. A common example of an MDI app is Microsoft Word.

When the different child forms within an MDI app don't refer to the same document, it's convenient to operate them in separate threads. This allows them to operate independently of each other.

To demonstrate the point, I created an app, MDIThread, in which each thread operates in its own child form.

Forms Designer work

The trick in laying out MDIThread is to modify Form1 so that it contains a single label, output control, start button, and stop button, instead of two sets of controls as defined in the MultiThread apps above. In addition, you double-click the active property closing to create the method Form1_closing(). (I'll explain this method later.) Form1 will be the child form.

In addition to Form1, it's necessary to create a new form to act as the MDI parent. To do so, click the Add Item toolbar button or choose Add Form on the Project menu. Select Form from the list of objects to add and give it the name MDIParent before clicking the Open button. This creates a new form class, MDIParent, contained in the file MDIParent.java. Visual J Plus Plus adds the new .java file to the project automatically.

Change the text property of the MDIParent form to Calc Primes Parent or something equally meaningful. Now add a MainMenu control from the Toolbox. To the main menu, add the menu item PrimeCalc. Under the PrimeCalc menu, add the menu items New and Exit, in that order. Change the name property of the New item to newMI and the Exit item to exitMI. Now double-click both of these menu items to create the menu item event handlers.

Since the Background class is unchanged from the MultiThread1 example, simply copy the Background.java file from the MultiThread1 directory to the MDIThread directory. Visual J Plus Plus automatically adds the file to the project.

Finally, choose MDIThread Properties from the Project menu. Select the Launch tab, and under the label When Project Runs, Load, select MDIParent from the dropdown list. Now select OK. Since MDIParent is the main form, execution starts there.

code for Form1

The code for the Form1.java file appears as follows:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * Implement a child form to the MDI parent MDIParent.
 */
public class Form1 extends Form
{
 // the background Thread object
 Background bg;
 public Form1(Form parent,
 int bgPriority)
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // attach this form to our parent form
 this.setMDIParent(parent);
 // make ourselves visible
 this.setVisible(true);
 // create a thread of specified priority
 bg = new Background(outputEdit1);
 bg.setPriority(bgPriority);
 // update the label to reflect the priority
 setLabel(bg, label1);
 // start the background thread
 bg.start();
 }
 /**
 * Update the specified label with the priority of thread.
 */
 private void setLabel(Thread thread, Label label)
 {
 // …same as in MultiThread examples…
 }
 private void button1_click(Object source, Event e)
 {
 bg.activate(true);
 }
 private void button2_click(Object source, Event e)
 {
 bg.activate(false);
 }
 private void Form1_closing(Object source, CancelEvent e)
 {
 // don't close the entire app
 e.cancel = true;
 // but do kill the background thread…
 bg.stop();
 // and dispose of the current form
 this.dispose();
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit outputEdit1 = new Edit();
 Label label1 = new Label();
 Button button1 = new Button();
 Button button2 = new Button();
 private void initForm()
 {
 // …created by Forms Designer…
 }
}


This version of Form1 has been modified in several ways. First, there is no main() method anymore. The app starts with the MDIParent class.

In addition, this program adds two arguments to the Form1() constructor: a reference to the MDI parent form, and the priority of the background thread. After the form has set itself up by calling initForm(), Form1() calls the method setMDIParent(parent). This establishes the current form as an MDI child of the parent form parent. Next the form makes itself visible by calling setVisible(true). Visual J Plus Plus creates forms as invisible so that the user won't see the individual objects get added to the form. The app.run() call in main() normally saves you the trouble of making the form visible.

Finally, Form1() sets up the background thread using the same Background thread class as in MultiThread1.

The remainder of Form1 is unchanged from the previous version except for the addition of the Form1_closing() method. This method is invoked before the form closes. Unfortunately, the default action of a form when closing is to dispose of itself and then call Thread.exit(), which terminates the entire app. All we want is the form to close itself. Setting the cancel flag in the CancelEvent object e to true (the default is false) tells the Form.closing() method not to do anything—that we will do it all. The next thing that Form1_closing() does is call bg.stop(). This kills the background thread and disposes of its resources. Finally, Form1_closing() calls dispose() to close the current form and dispose of its resources.

MDIParent code

The code for the MDIParent class is reasonably straightforward:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * Act as an MDI parent to Form1.
 */
public class MDIParent extends Form
{
 public MDIParent()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // set this form to be an MDI container
 setIsMDIContainer(true);
 }
 .
 .
 .
 private void newMI_click(Object source, Event e)
 {
 // create a child form object of slightly
 // less priority than we are
 Thread thread = Thread.currentThread();
 int priority = thread.getPriority() - 1;
 Form form = new Form1(this, priority);
 }
 private void exitMI_click(Object source, Event e)
 {
 app.exit();
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 MainMenu mainMenu1 = new MainMenu();
 MenuItem menuItem1 = new MenuItem();
 MenuItem newMI = new MenuItem();
 MenuItem exitMI = new MenuItem();
 private void initForm()
 {
 // …created by the Forms Designer…
 }
 /**
 * The main entry point for the app. * …
 */
 public static void main(String args[])
 {
 app.run(new MDIParent());
 }
}


The method main() executes the MDIParent form in the usual way. The constructor for MDIParent invokes initForm() and then calls setIsMDIContainer(true) to designate this form as an MDI container form.

The interesting code is in the two menu click event handlers. The newMI_click() method is invoked when the user chooses New from the PrimeCalc menu. The newMI_click() method creates a new Form1 child form with a background thread priority that is one less than the priority of the current thread. The exitMI_click() method terminates the current app by calling app.exit().

result

I find the result of this relatively simple program to be quite interesting. Figure 10-5 shows MDIThread after I have selected New from the PrimeCalc menu three times to create three independent child forms. Each of the child forms executes independently of the rest. You can also see that one of the child forms (the first one created) is currently the active child.

The user can close any one of the child forms—by clicking on the close icon in the upper right corner of that form—with no noticeable effect on the other two forms (except for a slight acceleration in both of them). In addition, since the background threads are executing at a lower priority than the parent thread, the child threads have no noticeable effect on the responsiveness of the parent form; the child threads tend to slow down or stop running as soon as the user moves the parent window.

Java Click to view at full size.

Screenshot-5. The MDIThread app executing with three active child forms.

Using the idle Event to Perform Background Processing

Setting the priority of a thread lower than the main app's priority ensures that the background threads will have minimal negative impact on the performance of the main thread. However, the app class offers a technique for creating a "thread" that is guaranteed to have no impact on the performance of any other thread.

As I have mentioned earlier, all user input is conveyed to the app in the form of Windows messages that Visual J Plus Plus converts into events. When the message queue is completely empty—that is, when there is absolutely no user input to process—Visual J Plus Plus invokes the idle event.

You can use the idle event to perform simple functions that you don't want to impact system performance. Since the idle event is a "one-shot" event (that is, it occurs once), it's basically not suitable for running in a loop such as our prime numbers loop. We can get around this one-shot problem relatively cleanly, however. (In any event, the problem will be fixed in an even better way in the next chapter.)

The following IdleEvent1 app uses the idle event to calculate prime numbers when there is no user input to process. In addition, IdleEvent1 adds a crude drawing program like that shown in to demonstrate that the idle process has no deleterious effect on the responsiveness of the system to user input.

Forms Designer work

First create the app IdleEvent1. Using the Forms Designer, update Form1 to look much like the child form in the MDIThread app. Now add a large panel on the bottom of the form below the buttons. Set the name property of the panel to paintPanel. Set the borderStyle property to Fixed 3D to enhance the appearance. Switch to the active properties, and double click the paint property to create the paint event handler paintPanel_paint().

Form1 code

The code for Form1 appears as follows:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
import com.ms.wfc.util.*;
/**
 * This class demonstrates the use of idle processing using
 * the onIdle() process.
 */
public class Form1 extends Form
{
 // define holder for the idle process handler
 IdleProcess idle;
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // Create an onIdle process
 idle = new IdleProcess(outputEdit);
 }
 .
 .
 .
 private void button1_click(Object source, Event e)
 {
 idle.setActive(true);
 }
 private void button2_click(Object source, Event e)
 {
 idle.setActive(false);
 }
 // The code from this point forward that handles
 // the drawing of squiggles in the panel is not
 // shown in this listing in the interest of brevity.
 // The complete program appears on the companion CD-ROM.
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit outputEdit = new Edit();
 Label label1 = new Label();
 Button button1 = new Button();
 Button button2 = new Button();
 Panel paintPanel = new Panel(components);
 private void initForm()
 {
 // …created by the Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


The majority of the source code to Form1 pertains to handling the painting of squiggles in the panel. Since this is similar to the FreeDraw app in , this code is not shown here in the interest of saving space.

The relevant section of Form1 is in the Form1() constructor where the IdleProcess object is passed. The outputEdit argument indicates the object to which prime numbers are to be written.

The start and stop button click event handlers turn the idle process on and off.

IdleProcess code

Add a class named IdleProcess to your project. Here is the code for that class:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
/**
 * Calculate primes "in the background" by attaching
 * to the idle event.
 */
public class IdleProcess
{
 // the WFC control to use for outputting primes
 Control output;
 // note whether idle loop is active or not
 boolean active = false;
 // the following timer is used to periodically "kick"
 // the system into invoking the idle loop
 Timer timer;
 // number of primes found and the last
 // candidate for 'primehood'
 int numberOfPrimes = 0;
 int lastCandidate = 0;
 /**
 * Create an idle process that reports primes back
 * to the parent through the output control.
 */
 public IdleProcess(Control output)
 {
 // save the parent
 this.output = output;
 // assign an on idle event handler
 app.addOnIdle(new EventHandler(this.onIdle));
 // set a timer to prompt system
 timer = new Timer();
 timer.setInterval(20);
 timer.setEnabled(true);
 }
 /**
 * Turn the idle process on and off.
 */
 public void setActive(boolean active)
 {
 this.active = active;
 }
 /**
 * Invoked when the system is idle.
 */
 public void onIdle(Object sender, Event e)
 {
 // if we're active…
 if (active)
 {
 // note the start time
 Time startTime = new Time();
 Time endTime = startTime.addMillis(10);
 // work until timer exits the loop
 while(true)
 {
 // if current time is greater than end time…
 Time currentTime = new Time();
 if (Time.compare(currentTime, endTime) > 0)
 {
 // get out of the loop and return
 break;
 }
 // loop for 100 candidates or so
 for (int i = 0; i < 100; i++)
 {
 // try the next number
 if (isPrime(++lastCandidate))
 {
 // it's prime - count it
 numberOfPrimes++;
 // report results back to parent
 output.setText(
 toString(numberOfPrimes) +
 "-" +
 toString(lastCandidate));
 }
 }
 }
 }
 }
 /**
 * Test next number for prime.
 */
 boolean isPrime(long number)
 {
 // …identical to earlier versions…
 }
 /**
 * Convert a number into a string with commas.
 */
 private String toString(long value)
 {
 // …identical to earlier versions…
 }
}


The isPrime() and toString() methods are identical to earlier versions. The remainder of the IdleProcess class has a completely different flavor.

The constructor for IdleProcess() saves the WFC Control object to which prime numbers are to be written. That done, IdleProcess() creates an EventHandler around the onIdle() method and assigns it to process the idle event. Finally, the constructor sets a Timer object to trigger every 20 milliseconds for reasons I'll explain later.

The actual onIdle() method is structured differently than the run() methods of the previous multithreaded examples. This is because the calculation of prime numbers in this example is occurring in the same thread as the main program; it's just that at the moment the main thread has nothing else to do. This has two implications:

The onIdle() method begins by first checking the active flag. If active is false, the prime calculation process is suspended and the onIdle() method returns without taking any action. If active is true, onIdle() continues by noting the current time and calculating an endTime some 10 milliseconds into the future. I arbitrarily decided that the onIdle() processor could work for 10 milliseconds at a time without any- one noticing.

The onIdle() method then enters a loop that periodically checks the time until the currentTime exceeds the endTime. As long as the endTime isn't exceeded, onIdle() calculates and reports prime numbers using the same algorithm as in earlier versions. The program checks 100 prime number candidates before rechecking the time, to increase performance. The computer can check 100 candidates in much less than 10 milliseconds. It's a waste to check the time after every candidate.

Once 10 milliseconds have gone by, onIdle() returns control to the app, saving the position in lastCandidate and the number of primes found so far in numberOfPrimes.

NOTE
It's critical that the onIdle() event handler finish processing, or be able to save sufficient information to allow it to pick up where it left off the next time it runs. Saving its position is called saving the state.

Why set a timer?

One curious feature of IdleEvent1 is the Timer object created in the IdleProcess constructor. This is especially interesting since I never defined a timerEvent handler to do anything with the timer event once it occurred.

This Timer object solves a problem inherent with any repetitive idle event handler. Each time the process goes idle, the idle event occurs only once. Once the onIdle() method returns to the caller (after the first 10 milliseconds), the main thread is blocked to give time to other, lower-priority threads. For many apps, this is okay. In our case, however, we want to continue to calculate primes as long as the system isn't busy.

The Timer object sort of "kicks" the main thread by forcing the thread to process a "do nothing" timer event. After every timer event, if the app is still idle, the system passes control to the onIdle() process again.

So if the onIdle() process executes for 10 milliseconds, why is Timer set for 20 milliseconds? If Timer were set for the same 10 milliseconds that onIdle() uses, the onIdle() method would keep the main thread busy all the time, thereby never giving control to any lower-priority threads. Comments