Previous | Next | Trail Map | To 1.1 -- And Beyond! | A Preview of Things to Come

Threads Changes

The Thread.stop, Thread.suspend, and Thread.resume methods will be deprecated in the next major release of the JDK.

Thread.stop is being deprecated because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that the program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.

This tutorial has always recommended against using Thread.stop. So, hopefully, this change will not affect your programs.

If however, you 've been using Thread.stop in your programs, you should substitute that use with code that provides for a gentler termination. Most uses of stop can and should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running.

For example, suppose your applet contains the following start, stop and run methods:

public void start() {
    blinker = new Thread(this);
    blinker.start();
}

public void stop() {
    blinker.stop();  // UNSAFE!
}

public void run() {
    Thread thisThread = Thread.currentThread();
    while (true) {
        try {
	    Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}
You can avoid the use of Thread.stop by replacing the applet's stop and run methods with:
public void stop() {
    blinker = null;
}

public void run() {
    Thread thisThread = Thread.currentThread();
    while (blinker == thisThread) {
        try {
	    Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}
Thread.suspend is inherently deadlock-prone so it is also being deprecated, thereby necessitating the deprecation of Thread.resume. If the target thread holds a lock on the monitor protecting a critical system resource when it is suspended, no thread can access this resource until the target thread is resumed. If the thread that would resume the target thread attempts to lock this monitor prior to calling resume, deadlock results. Such deadlocks typically manifest themselves as "frozen" processes.

As with Thread.stop, the prudent approach is to have the "target thread" poll a variable indicating the desired state of the thread (active or suspended). When the desired state is suspended, the thread waits using Object.wait. When the thread is resumed, the target thread is notified using Object.notify.

For example, suppose your applet contains the following mousePressed event handler, which toggles the state of a thread called blinker:

Public void mousePressed(MouseEvent e) {
    e.consume();

    if (threadSuspended)
        blinker.resume();
    else
        blinker.suspend();  // DEADLOCK-PRONE!

    threadSuspended = !threadSuspended;
}
You can avoid the use of Thread.suspend and Thread.resume by replacing the event handler above with:
public synchronized void mousePressed(MouseEvent e) {
    e.consume();

    threadSuspended = !threadSuspended;

    if (!threadSuspended)
        notify();
}
and adding the following code to the "run loop":
synchronized(this) {
    while (threadSuspended)
	wait();
}
The wait method throws the InterruptedException, so it must be inside a try ... catch clause. It's fine to put it in the same clause as the sleep. The check should follow (rather than precede) the sleep so the window is immediately repainted when the the thread is "resumed." The resulting run method follows:
public void run() {
    while (true) {
        try {
            Thread.sleep(interval);

            synchronized(this) {
                while (threadSuspended)
                    wait();
            }
        } catch (InterruptedException e){
        }
        repaint();
    }
}
Note that the notify in the mousePressed method and the wait in the run method are inside synchronized blocks. This is required by the language, and ensures that wait and notify are properly serialized. In practical terms, this eliminates race conditions that could cause the "suspended" thread to miss a notify and remain suspended.


Note: Don't wait for the next major release of the JDK. These are changes that you can make to improve your programs NOW!


Previous | Next | Trail Map | To 1.1 -- And Beyond! | A Preview of Things to Come