0

I am getting into multithreading in Java, and I ran into an odd problem.

When I try to start a new thread with my scheduler using Thread.start(), I learned that we need a Thread.isAlive() check because starting and resuming take different processes. however, with this code showing the check and start():

if ( current.isAlive( ) )
     current.setPriority( 4 );
else {
     // Spawn must be controlled by Scheduler
     // Scheduler must start a new thread
     current.start( );
     current.setPriority( 4 );
}

where current is guaranteed to be a valid thread. I end up with this error

Exception in thread "Thread-0" java.lang.IllegalThreadStateException
        at java.base/java.lang.Thread.start(Thread.java:790)
        at Scheduler.run(Scheduler.java:152)

Which is why I think it's a problem of calling start() when it's not supposed to . However, to all appearances, it shouldn't be calling start() except when the thread is not alive.

Thus, my question is two-fold:

  1. Is there something wrong with the above code that allows the second block to run if a thread is already started, and

  2. What other things could be going wrong that would throw that exception? I see the same exception thrown in setDaemon(), however I'm not calling that, so that should be irrelevant.

user207421
  • 305,947
  • 44
  • 307
  • 483
awsirkis
  • 389
  • 6
  • 19
  • 5
    You learnt wrong. `isAlive()` tells you whether it has been started and hasn't finished yet. It doesn't tell you whether or not you can call `start()`. You can't reuse `Thread` objects, and specifically you can't call `start()` on them twice. And there is no such thing as 'resuming' a thread. – user207421 Nov 02 '19 at 07:04
  • 4
    The [start](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#start--) method "Throws: IllegalThreadStateException - if the thread was already started." – D.B. Nov 02 '19 at 07:04
  • 1
    And you can only call `setPriority(4);` if the Thread has not been started yet. – dan1st Nov 02 '19 at 07:35

2 Answers2

5

A Thread has a State, retrievable by calling getState(). The javadoc of State shows:

A thread state. A thread can be in one of the following states:

  • NEW
    A thread that has not yet started is in this state.
  • RUNNABLE
    A thread executing in the Java virtual machine is in this state.
  • BLOCKED
    A thread that is blocked waiting for a monitor lock is in this state.
  • WAITING
    A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
  • TIMED_WAITING
    A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
  • TERMINATED
    A thread that has exited is in this state.

A thread can be in only one state at a given point in time. These states are virtual machine states which do not reflect any operating system thread states.

When you call start(), the thread leaves the NEW state, and will never have that state ever again. Javadoc of start() says:

Throws IllegalThreadStateException if the thread was already started.

That means if the state is not NEW.

However, javadoc of isAlive() says:

Tests if this thread is alive. A thread is alive if it has been started and has not yet died.

Which means that the thread is not NEW (has been started) and not TERMINATED (has not yet died)

As you can see, isAlive() will return false for TERMINATED, even though TERMINATED is not a valid state for starting the thread.

If you need to know if you can start the thread, check the state:

if (thread.getState() == Thread.State.NEW) {
    thread.start();
    // Ok, the thread is now beginning to work on it
} else if (thread.getState() != Thread.State.TERMINATED) {
    // Yay, the thread is working on it
} else {
    // Oops, what happened???
]
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
Andreas
  • 154,647
  • 11
  • 152
  • 247
2

The problem with your approach is that you cannot "reuse" Thread instances so you cannot call start method on the same instance of Thread more than once. From Thread:::isAlive docs :

Tests if this thread is alive. A thread is alive if it has been started and has not yet died.

So you can fall into your else condition if your Thread was already started and died (and also when it has not been started yet). And you will get an exception in case you try to start it once again because IllegalThreadStateException is thrown if you try to do it. From the docs of Thread::start:

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

Throws: IllegalThreadStateException - if the thread was already started.

If you would like to pause/resume threads you can do it by using wait/notifyAll in a correct manner - see this question.

Community
  • 1
  • 1
Michał Krzywański
  • 15,659
  • 4
  • 36
  • 63