Thread Properties in Java

In the following sections, we discuss miscellaneous properties of threads: the interrupted status, daemon threads, handlers for uncaught exceptions, as well as some legacy features that you should not use.

1. Interrupting Threads

A thread terminates when its run method returns—by executing a return state­ment, after executing the last statement in the method body, or if an exception occurs that is not caught in the method. In the initial release of Java, there also was a stop method that another thread could call to terminate a thread. However, that method is now deprecated. We discuss the reason in Section 12.4.13, “Why the stop and suspend Methods Are Deprecated,” on p. 779.

Other than with the deprecated stop method, there is no way to force a thread to terminate. However, the interrupt method can be used to request termination of a thread.

When the interrupt method is called on a thread, the interrupted status of the thread is set. This is a boolean flag that is present in every thread. Each thread should occasionally check whether it has been interrupted.

To find out whether the interrupted status was set, first call the static Thread.currentThread method to get the current thread, and then call the isInterrupted method:

white (!Thread.currentThread().isInterrupted() && more work to do)

{

do more work

}

However, if a thread is blocked, it cannot check the interrupted status. This is where the InterruptedException comes in. When the interrupt method is called on a thread that blocks on a call such as steep or wait, the blocking call is ter­minated by an InterruptedException. (There are blocking I/O calls that cannot be interrupted; you should consider interruptible alternatives. See Chapters 2 and 4 of Volume II for details.)

There is no language requirement that a thread which is interrupted should terminate. Interrupting a thread simply grabs its attention. The interrupted thread can decide how to react to the interruption. Some threads are so im­portant that they should handle the exception and continue. But quite com­monly, a thread will simply want to interpret an interruption as a request for termination. The run method of such a thread has the following form:

Runnable r = () -> {

try

{

while (!Thread.currentThread().isInterrupted() && more work to do)

{

do more work

}

}

catch(InterruptedException e)

{

// thread was interrupted during steep or wait

}

finatty

{

cleanup, if required

}

// exiting the run method terminates the thread

};

The isInterrupted check is neither necessary nor useful if you call the steep method (or another interruptible method) after every work iteration. If you call the sleep method when the interrupted status is set, it doesn’t sleep. In­stead, it clears the status (!) and throws an InterruptedException. Therefore, if

your loop calls sleep, don’t check the interrupted status. Instead, catch the InterruptedException, like this:

Runnable r = () -> {

try

{

while (more work to do)

{

do more work

Thread.sleep(delay);

}

}

catch(InterruptedException e)

{

// thread was interrupted during sleep

}

finally

{

cleanup, if required

}

// exiting the run method terminates the thread

};

You’ll find lots of published code in which the InterruptedException is squelched at a low level, like this:

void mySubTask()

{

try { sleep(delay); }

catch (InterruptedException e) {} // don’t ignore!

}

Don’t do that! If you can’t think of anything good to do in the catch clause, you still have two reasonable choices:

  • In the catch clause, call Thread.currentThread().interrupt() to set the interrupted status. Then the caller can test it.

void mySubTask()

{

try { sleep(delay); }

catch (InterruptedException e) { Thread.currentThread().interrupt(); }

}

  • Or, even better, tag your method with throws InterruptedException and drop the try block. Then the caller (or, ultimately, the run method) can catch it.

void mySubTask() throws InterruptedException

{

sleep(delay);

}

2. Daemon Threads

You can turn a thread into a daemon thread by calling t.setDaemon(true);

There is nothing demonic about such a thread. A daemon is simply a thread that has no other role in life than to serve others. Examples are timer threads that send regular “timer ticks” to other threads or threads that clean
up stale cache entries. When only daemon threads remain, the virtual machine exits. There is no point in keeping the program running if all remaining threads are daemons.

3. Thread Names

By default, threads have catchy names such as Thread-2. You can set any name with the setName method:

var t = new Thread(runnabte);

t.setName(“Web crawler”);

That can be useful in thread dumps.

4. Handlers for Uncaught Exceptions

The run method of a thread cannot throw any checked exceptions, but it can be terminated by an unchecked exception. In that case, the thread dies.

However, there is no catch clause to which the exception can be propagated. Instead, just before the thread dies, the exception is passed to a handler for uncaught exceptions.

The handler must belong to a class that implements the Thread .UncaughtExceptionHandler interface. That interface has a single method,

void uncaughtException(Thread t, Throwable e)

You can install a handler into any thread with the setUncaughtExceptionHandler method. You can also install a default handler for all threads with the static method setDefaultUncaughtExceptionHandler of the Thread class. A replacement handler might use the logging API to send reports of uncaught exceptions into a log file.

If you don’t install a default handler, the default handler is null. However, if you don’t install a handler for an individual thread, the handler is the thread’s ThreadGroup object.

The ThreadGroup class implements the Thread.UncaughtExceptionHandter interface. Its uncaughtException method takes the following action:

  1. If the thread group has a parent, then the uncaughtException method of the parent group is called.
  2. Otherwise, if the Thread.getDefauttUncaughtExceptionHandter method returns a non-null handler, it is called.
  3. Otherwise, if the Throwabte is an instance of ThreadDeath, nothing happens.
  4. Otherwise, the name of the thread and the stack trace of the Throwabte are printed on System.err.

That is the stack trace that you have undoubtedly seen many times in your

5. Thread Priorities

In the Java programming language, every thread has a priority. By default, a thread inherits the priority of the thread that constructed it. You can increase or decrease the priority of any thread with the setPriority method. You can set the priority to any value between MIN_PRIORITY (defined as 1 in the Thread class) and MAX_PRIORITY (defined as 10). NORM_PRIORITY is defined as 5.

Whenever the thread scheduler has a chance to pick a new thread, it prefers threads with higher priority. However, thread priorities are highly system- dependent. When the virtual machine relies on the thread implementation of the host platform, the Java thread priorities are mapped to the priority levels of the host platform, which may have more or fewer thread priority levels.

For example, Windows has seven priority levels. Some of the Java priorities will map to the same operating system level. In the Oracle JVM for Linux, thread priorities are ignored altogether—all threads have the same priority.

Thread priorities may have been useful in early versions of Java that didn’t use operating systems threads. You should not use them nowadays.

Source: Horstmann Cay S. (2019), Core Java. Volume I – Fundamentals, Pearson; 11th edition.

Leave a Reply

Your email address will not be published. Required fields are marked *