• No results found

The Thread API

In document JAVA (Page 156-159)

The Types class determines any constants that are used to identify SQL types

UNIT 13: MULTITHREADED APPLICATION

13.6 The Thread API

The following sections present a detailed analysis of the Java Thread API.

Constructors

The Thread class has seven different constructors:

public Thread();

public Thread(Runnable target);

public Thread(Runnable target, String name);

public Thread(String name);

public Thread(ThreadGroup group, Runnable target);

public Thread(ThreadGroup group, Runnable target, String name);

public Thread(ThreadGroup group, String name);

These constructors represent most of the combinations of three different parameters: thread name, thread group, and a Runnable target object. To understand the constructors, you must understand the three parameters:

name is the (string) name to be assigned to the thread. If you fail to specify a name, the system generates a unique name of the form Thread-N, where N is a unique integer.

target is the Runnable instance whose run() method is executed as the main method of the thread.

group is the ThreadGroup to which this thread will be added. (The ThreadGroup class is discussed in detail later in this chapter.)

Constructing a new thread does not begin the execution of that thread. To launch the Thread object, you must invoke its start() method.

When creating a thread, the priority and daemon status of the new thread are set to the same values as the thread from which the new thread was created.

CAUTION: Although it is possible to allocate a thread using new Thread(), it is not useful to do so. When constructing a thread directly (without subclassing), the Thread object requires a target Runnable object because the Thread class itself does not contain your application's logic.

Naming

public final String getName();

public final void setName(String name);

Every Java thread has a name. The name can be set during construction or with the setName() method. If you fail to specify a name during construction, the system generates a unique name of the form Thread-N, where N is a unique integer; the name can be changed later using setName().

The name of a thread can be retrieved using the getName() method.

Thread names are important because they provide the programmer with a useful way to identify particular threads during debugging. You should name a thread in such a way that you (or others) will find the name helpful in identifying the purpose or function of the thread during debugging.

Starting and Stopping

To start and stop threads once you have created them, you need the following methods:

public void start();

public final void stop();

public final void stop(Throwable obj);

public void destroy();

To begin a new thread, create a new Thread object and call its start() method. An exception is thrown if start() is called more than once on the same thread.

As discussed in "Thread States," earlier in this chapter, there are two main ways a thread can terminate: The thread can return from its run() method, ending gracefully. Or the thread can be terminated by the stop() or destroy() method.

When invoked on a thread, the stop() method causes that thread to terminate by throwing an exception to the thread (a ThreadDeath exception). Calling stop() on a thread has the same behavior as executing throw new ThreadDeath() within the thread, except that stop() can also be called from other threads (whereas the throw statement affects only the current thread).

To understand why stop() is implemented this way, consider what it means to stop a running thread. Active threads are part of a running program, and each runnable thread is in the middle of doing something. It is likely that each thread is consuming system resources: file descriptors, graphics contexts, monitors (to be discussed later), and so on. If

stopping a thread caused all activity on the thread to cease immediately, these resources might not be cleaned up properly. The thread would not have a chance to close its open files or release the monitors it has locked. If a thread were stopped at the wrong moment, it would be unable to free these resources; this leads to potential problems for the virtual machine (running out of open file descriptors, for example).

To provide for clean thread shutdown, the thread to be stopped is given an opportunity to clean up its resources. A ThreadDeath exception is thrown to the thread, which percolates up the thread's stack and through the exception handlers that are currently on the stack (including finally blocks). Monitors are also released by this stack-unwinding process.

The following Listing shows how calling stop() on a running thread generates a ThreadDeath exception.

Listing. Generating a ThreadDeath exception with stop().

class DyingThread extends Thread { // main(), this class is an application public static void main(String[] args) {

Thread t = new DyingThread(); // create the thread

The DyingThread class has two parts. The main() method spawns a new DyingThread, waits for a period of time, and then sends a stop() to the thread. The DyingThread run() method, which is executed in the spawned thread, opens a file and periodically writes output to that file. When the thread receives the stop(), it catches the ThreadDeath exception and closes the open file. It then rethrows the ThreadDeath exception.

When you run the code shown in Listing 6.3, you see the following output:

Cleaning up.

NOTE: Java provides a convenient mechanism for programmers to write "cleanup" code--code that is executed when errors occur or when a program or thread terminates. (Cleanup involves closing open files, disposing of graphics contexts, hiding windows, and so on.) Exception handler catch and finally blocks are good locations for cleanup code.

Programmers use a variety of styles to write cleanup code. Some programmers place cleanup code in catch(ThreadDeath td) exception handlers (as was done in the above Listing). Others prefer to use catch(Throwable t) exception handlers. Both these methodsare good, but writing cleanup code in a finally block is the best solution for most situations. A finally block is executed unconditionally, whether the exception handler exited because of a thrown exception or not. If an exception was thrown, it is automatically rethrown after the finally block has completed.

Although the ThreadDeath solution allows the application a high degree of flexibility, there are problems. By catching the ThreadDeath exception, a thread can actually prevent stop() from having the desired effect. The code to do this is trivial:

// prevent stop() from working catch (ThreadDeath td) {

System.err.println("Just try to stop me. I'm invincible.");

// oh no, I've failed to rethrow td }

Calling stop() is not sufficient to guarantee that a thread will end. This is a serious problem for Java-enabled Web browsers; there is no guarantee that an applet will terminate when stop() is invoked on a thread belonging to the applet.

The destroy() method is stronger than the stop() method. The destroy() method is designed to terminate the thread without resorting to the ThreadDeath mechanism. The destroy() method stops the thread immediately, without cleanup; any resources held by the thread are not released.

CAUTION: The

destroy()

method is not implemented in the Java Development Kit, in all versions up to and including 1.1. Calling this method results in a

NoSuchMethodError

exception. Although there has been no comment about when this method will be implemented, it is likely that it will not become available until JavaSoft can implement it in a way that cleans up the dying thread's environment (locked monitors, pending I/O, and so on).

In document JAVA (Page 156-159)