JDK 1.1 for Solaris Developer's Guide

Deprecated Threads Methods

The Thread.stop, Thread.suspend, and Thread.resume methods are deprecated as of JDK 1.1. 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 was in an inconsistent state, other threads might view these objects in an inconsistent state. Such objects are said to be damaged.

Threads operating on damaged objects can behave arbitrarily, either obviously or not. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that the program might be corrupted. The corruption can manifest itself at an unpredictable time after the damage occurs.

Substitute any use of Thread.stop with code that provides for a gentler termination. Most uses of stop() can and should be replaced by code that modifies a variable indicating that the target thread should stop running. The target thread should check this variable regularly. If the variable indicates that the thread is to stop, the thread should then return from its run() method in an orderly fashion. 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. Thus, the deprecation of Thread.resume is also necessary. 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 correct 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. You can also 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 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();
		}
} 

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.