There was a question on IllegalMonitorStateException and thread states in CodeRanch (http://www.coderanch.com/t/616837/java-programmer-SCJP/certification/state). Since it is a general topic of interest to most OCPJP7 aspirants, I'm posting the expanded version of the answer I gave as blog entry here.
A program can access the state of the thread using Thread.State enumeration. The Thread class has the getState() instance method which returns the current state of the thread. Here is an example:
class BasicThreadStates extends Thread {
public static void main(String []s) throws Exception {
Thread t = new Thread(new BasicThreadStates());
System.out.println("Just after creating thread; \n" +
" The thread state is: " + t.getState());
t.start();
System.out.println("Just after calling t.start(); \n" +
" The thread state is: " + t.getState());
t.join();
System.out.println("Just after main calling t.join(); \n" +
" The thread state is: " + t.getState());
}
}
Just after creating thread;
The thread state is: NEW
Just after calling t.start();
The thread state is: RUNNABLE
Just after main calling t.join();
The thread state is: TERMINATED
Just after the creation of the thread and just before calling the start() method on that thread, the thread is in the new state. After calling the start() method, the thread is ready to run or is in the running state (which we cannot determine); so it is in runnable state. From the main() method, we are calling t.join(). The main() method waits for the thread t to die. So once the statement t.join() successfully gets executed by main() thread, it means that the thread t has died or terminated. So, the thread is in the terminated state now.
A word of advice: be careful about accessing the thread states using the getState() method. Why? By the time you acquire information on a thread state and print it, the state could have changed! I know the last statements could be confusing. To understand the problem with getting thread state information using the getState() method, consider the previous example. In one sample run of the same program, it printed the following:
Just after creating thread;
The thread state is: NEW
Just after calling t.start();
The thread state is: TERMINATED
Just after main calling t.join();
The thread state is: TERMINATED
Note the red italicized part of the output, the statement after printing “Just after calling t.start();”. In the initial output, we got the thread state (as expected) as RUNNABLE state. However, in another execution of the same program without any change, it printed the state as TERMINATED. Why? In this case, the thread is dead before we could get a chance to check it and print its status! [Note that we have not implemented the run() method in the BasicThreadStates class, so the default implementation of the run() method does nothing, and terminates quickly.]
class ThreadStatesEnumeration {
public static void main(String []s) {
for(Thread.State state : Thread.State.values()){
System.out.println(state);
}
}
}
It prints:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
This program crashes like this:
Basic thread states
A thread has various states during its lifetime. Three basic thread states to understand are – new, runnable and terminated. We will discuss more thread states a bit later.A program can access the state of the thread using Thread.State enumeration. The Thread class has the getState() instance method which returns the current state of the thread. Here is an example:
class BasicThreadStates extends Thread {
public static void main(String []s) throws Exception {
Thread t = new Thread(new BasicThreadStates());
System.out.println("Just after creating thread; \n" +
" The thread state is: " + t.getState());
t.start();
System.out.println("Just after calling t.start(); \n" +
" The thread state is: " + t.getState());
t.join();
System.out.println("Just after main calling t.join(); \n" +
" The thread state is: " + t.getState());
}
}
This program prints:
Just after creating thread;
The thread state is: NEW
Just after calling t.start();
The thread state is: RUNNABLE
Just after main calling t.join();
The thread state is: TERMINATED
Just after the creation of the thread and just before calling the start() method on that thread, the thread is in the new state. After calling the start() method, the thread is ready to run or is in the running state (which we cannot determine); so it is in runnable state. From the main() method, we are calling t.join(). The main() method waits for the thread t to die. So once the statement t.join() successfully gets executed by main() thread, it means that the thread t has died or terminated. So, the thread is in the terminated state now.
A word of advice: be careful about accessing the thread states using the getState() method. Why? By the time you acquire information on a thread state and print it, the state could have changed! I know the last statements could be confusing. To understand the problem with getting thread state information using the getState() method, consider the previous example. In one sample run of the same program, it printed the following:
Just after creating thread;
The thread state is: NEW
Just after calling t.start();
The thread state is: TERMINATED
Just after main calling t.join();
The thread state is: TERMINATED
More thread states
A thread can also be in blocked, waiting, timed_waiting states—which we’ll discuss now. I've attached a figure with this post which shows how and when the state transitions typically happen for these six states. You can use Thread.State enumeration which has the list of possible thread states. Here is a simple program that prints the value of the states in this enumeration:class ThreadStatesEnumeration {
public static void main(String []s) {
for(Thread.State state : Thread.State.values()){
System.out.println(state);
}
}
}
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
Now let us discuss exceptions.
IllegalThreadStateException
Here is a program that throws IllegalThreadStateException
class ThreadStateProblem {
public
static void main(String []s) {
Thread
thread = new Thread();
thread.start();
thread.start();
}
}
The program fails with this stack
trace:
Exception in thread "main"
java.lang.IllegalThreadStateException
at
java.lang.Thread.start(Unknown Source)
at
ThreadStateProblem.main(ThreadStateProblem.java:6)
Here, we are trying to start a
thread that has already started. When we call start(), the
thread moves to “new” state. There is no proper state transition from “new”
state if we call start() again; so
the JVM throws IllegalThreadStateException.
IllegalMonitorStateException
Here is a program that results in a IllegalMonitorStateException:
class ThreadStateProblem extends Thread {
public void
run() {
try
{
wait(1000);
}
catch(InterruptedException
ie) {
//
its okay to ignore this exception since we’re not
//
interrupting exceptions in this code
ie.printStackTrace();
}
}
public
static void main(String []s) {
new
ThreadStateProblem().start();
}
}
Exception in thread "Thread-0"
java.lang.IllegalMonitorStateException
at
java.lang.Object.wait(Native Method)
at
ThreadStateProblem.run(ThreadStateProblem.java:4)
The wait(int)
method (with or without timeout value) should be called only after acquiring a
lock: a wait() call adds the thread to the waiting queue of the acquired lock.
If we don’t do that, there is no proper transition from the running state to timed_waiting (or waiting
state in case timeout value in not given) can happen. So, the program crashes by
throwing IllegalMonitorStateException
exception.
The correct fix is to acquire the lock
before calling wait(). In this
case, we can declare the run() method synchronized:
synchronized public void run() {
try {
wait(1000);
}
catch(InterruptedException
ie) {
//
its okay to ignore this exception since we’re not
//
interrupting exceptions in this code
ie.printStackTrace();
}
}
Since the run() method
is synchronized,
the wait()
will add itself to the this object reference lock. Since
there is no one calling the notify()/notifyAll()
method, after timeout of 1 second (1000 milliseconds) is over, it will return
from the run() method. So, the wait(1000);
statement behaves almost like sleep(1000) statement; the difference
is that calling wait() releases
the lock on this object when it waits while sleep()
call will not release the lock when it sleeps.
So, the key observation is that: We must call wait and notify/notifyAll only after acquiring the relevant lock.
Difference between these exceptions
If you already did not recognize, IllegalThreadStateException is different from IllegalMonitorStateException.
Here is the description of IllegalThreadStateException from JavaDoc: "Thrown to indicate that a thread is not in an appropriate state for the requested operation. See, for example, the
suspend
and resume
methods in class Thread
."
Here is the description of IllegalMonitorStateException from JavaDoc: "Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor."
In both the cases, we need to be careful about thread states. In case of IllegalThreadStateException, it is about attempted illegal transition in thread states (and is nothing to do with locks). Whereas with IllegalMonitorStateException, it is about holding a lock - it occurs when attempting to call methods such as wait or notify on an object that does not hold the lock.
No comments:
Post a Comment