Thread States
Understanding the lifecycle of a thread can be beneficial when programming with threads. Threads can exist in different states. Just because the start() method has been called on the thread, it does not mean that the thread has access to the CPU and can start executing straightaway. Several factors determine how it will proceed.
Figure 22.4 shows the states and the transitions in the lifecycle of a thread. The figure shows the main transitions that occur during the lifecycle of a thread—in particular, transitions pertaining to blocked I/O (p. 1405) and thread interruption (p. 1393) have been omitted.
The thread states are represented by enum constants and summarized in Table 22.1. The Thread class provides the getState() method to determine the state of the current thread. The method returns a constant of type Thread.State (i.e., the type State is a static inner enum type declared in the Thread class).
Figure 22.4 Thread States
Table 22.1 Thread States Defined by the Thread.State Enum Type
Constant in the Thread.State enum type (see Figure 22.4) | Description of the state |
NEW | A thread in this state has been created, but it has not yet started. A thread is started by calling its start() method (p. 1370). |
RUNNABLE | The RUNNABLE states can be characterized by two substates which are not observable: READY substate: A thread starts life in the READY substate (p. 1386), and also when it transitions from a non-runnable state to the RUNNABLE state.RUNNING substate: If a thread is in the RUNNING substate, it means that the thread is currently executing (p. 1386), and is deemed the running thread. From the RUNNING substate, a thread can transition either to the READY substate, to a non-runnable state, or to the TERMINATED state. |
BLOCKED | A thread is blocked while waiting to acquire a lock (p. 1380, p. 1388). A thread is blocked while waiting for I/O (p. 1405). |
WAITING | A thread is in this state indefinitely for the following reasons, until the action it is waiting for occurs: A thread is waiting indefinitely for join completion: The thread awaits completion of another thread (p. 1402).A thread is waiting indefinitely for notification: The thread awaits notification from another thread (p. 1396). |
TIMED_WAITING | A thread is in this state for at least a specified amount of time for the following reasons, unless the action it is waiting for occurs before timeout: Timed waiting for join completion: The thread awaits completion of another thread (p. 1402).Timed waiting for notification: The thread awaits notification from another thread (p. 1396).Timed waiting to wake up from sleep (p. 1395). |
TERMINATED | The run() method completed execution or terminated. Once in this state, the thread can never run again (p. 1405). |
The rest of this section will expound on the thread states and the transitions between them during the lifecycle of a thread. However, the following remarks on the thread lifecycle in Figure 22.4 should be noted.
- The RUNNABLE state is a compound state having two non-observable substates: READY and RUNNING. Being non-observable means it is not possible to distinguish which substate the thread is in once it is in the RUNNABLE state.
- The following states are characterized as non-runnable states: BLOCKED, WAITING, and TIMED_WAITING. A running thread—one in the RUNNING substate—can transit to one of the non-runnable states, depending on the circumstances. A thread remains in a non-runnable state until a specific transition occurs. A thread does not go directly to the RUNNING substate from a non-runnable state, but transits first to the READY substate.
Selected methods from the Thread class are presented below. Examples of their usage are presented in subsequent sections.
final boolean isAlive()
This method can be used to find out if a thread is alive or dead. A thread is alive if it has been started but not yet terminated—that is, it is not in the TERMINATED state.
Thread.State getState()
Returns the state of this thread (Table 22.1). It should be used for monitoring the state and not for synchronizing control.
final int getPriority()
final void setPriority(int newPriority)
The first method returns the priority of a thread (p. 1385). The second method changes its priority. The priority set will be the minimum of the specified newPriority and the maximum priority permitted for this thread. There is no guarantee that a thread with a higher priority will be chosen to run.
static void yield()
Causes the current thread to temporarily pause its execution and, thereby, allow other threads to execute. It is up to the JVM to decide if and when this transition will take place. The transition is from the RUNNING substate to the READY substate in the RUNNABLE state (Figure 22.4).
static void sleep(long millisec) throws InterruptedException
static void sleep(long millis, int nanos) throws InterruptedException
The current thread sleeps for at least the specified time before it becomes eligible for running again. The transition is from the RUNNING substate of the RUNNABLE state to the TIMED_WAITING state (Figure 22.4).
final void join() throws InterruptedException
final void join(long millisec) throws InterruptedException
A call to any of these two methods invoked on a thread will wait and not return until either the thread has completed or it is timed out after the specified time, respectively. In Figure 22.4, the transition is from the RUNNING substate of the RUNNABLE state to the WAITING state for the first method and to the TIMED_WAITING state for the second method.
static Map<Thread, StackTraceElement[]> getAllStackTraces()
Returns a map of stack traces for all alive threads. Keep in mind that each thread has its own JVM stack for method execution. The map can be used, for example, to extract a set of all alive threads.
Example 22.3 illustrates transitions between thread states. A thread at (1) sleeps a little at (2) and then does some computation in a loop at (3), after which the thread terminates. The main() method monitors the thread in a loop at (4), printing the thread state returned by the getState() method. The output shows that the thread starts off in the NEW state and goes through the RUNNABLE state when the run() method starts to execute, and then transits to the TIMED_WAITING state to sleep. On waking up, it computes the loop in the RUNNABLE state, and transits to the TERMINATED state when the run() method completes execution.
Example 22.3 Thread States
public class ThreadStates {
private static Thread t1 = new Thread(“T1”) { // (1)
@Override public void run() {
try {
sleep(1); // (2)
for (int i = 10000; i > 0; i–) {;} // (3)
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
};
public static void main(String[] args) {
System.out.println(t1.getState()); // (4)
t1.start();
while (true) { // (5)
Thread.State state = t1.getState();
System.out.println(state);
if (state == Thread.State.TERMINATED) {
break;
}
}
}
}
Probable output from the program:
NEW
RUNNABLE
TIMED_WAITING
…
TIMED_WAITING
RUNNABLE
…
RUNNABLE
TERMINATED