views:

152

answers:

7

I know that it is not possible to restart a used Java Thread object, but I don't find an explanation why this is not allowed; even if it is guaranteed that the thread has finished (see example code below).

I don't see why start() (or at least a restart()) method should not be able to somehow reset the internal states - whatever they are - of a Thread object to the same values they have when the Thread object is freshly created.

Example code:

class ThreadExample {

  public static void main(String[] args){

    Thread myThread = new Thread(){
      public void run() {
        for(int i=0; i<3; i++) {
          try{ sleep(100); }catch(InterruptedException ie){}
          System.out.print(i+", ");
        }
        System.out.println("done.");
      }
    };

    myThread.start();

    try{ Thread.sleep(500); }catch(InterruptedException ie){}
    System.out.println("Now myThread.run() should be done.");

    myThread.start(); // <-- causes java.lang.IllegalThreadStateException

  } // main

} // class
+1  A: 

Why don't you want to create a new Thread? If you're concerned about the overhead of creating your MyThread object, make it a Runnable and run it with a new Thread(myThread).start();

Paul Tomblin
That still incurs **on the same thread creation overhead** once the runnable's run() method comes to an end (or ends abruptly.) The way to avoid such overhead is by creating thread pools or custom thread classes that wait till they get *messaged* with a `Runnable` (or some other command-like object) to run. Once it ends, the enclosing Thread goes to sleep until it gets/receives another one. Or a pool of consumer custom Threads consuming `Runnable` objects placed in a queue.
luis.espinal
This still requires the creation of new (and garbage collection of old) Thread objects.
Curd
@luis.espinal: Good point. Why don't you write this as an answer?
sleske
@Curd: No, not the way luis.espinal describes it. The actual thread (i.e. the instance `java.lang.Thread`) never terminates, it justs waits when it's done, until it receives a new `Runnable`. Only the `Runnable`s get created and GCed, and these can be as lightweight as you want/need.
sleske
The thread creation overhead is unavoidable.
Paul Tomblin
+2  A: 

Because they didn't design it that way. From a clarity standpoint, that makes sense to me. A Thread represents a thread of execution, not a task. When that thread of execution has completed, it has done its work and it just muddies things were it to start at the top again.

A Runnable on the other hand represents a task, and can be submitted to many Threads as many times as you like.

Mark Peters
+11  A: 

I'd pose the question the other way round - why should a Thread object be restartable?

It's arguably much easier to reason about (and probably implement) a Thread that simply executes its given task exactly once and is then permanently finished. To restart threads would require a more complex view on what state a program was in at a given time.

So unless you can come up with a specific reason why restarting a given Thread is a better option than just creating a new one with the same Runnable, I'd posit that the design decision is for the better.

(This is broadly similar to an argument about mutable vs final variables - I find the final "variables" much easier to reason about and would much rather create multiple new constant variables rather than reuse existing ones.)

Andrzej Doyle
One reason for the desire of `restart` is the cost of creating new (and garbage collecting old) Thread objects.
Curd
Then just use a thread pool where each thread can be reused across multiple Runnables.
Steve Kuo
as Steve says, use a thread pool mechanism. I love the ease of use of the ExecutorService implementations that can be obtained through the static methods of the Executors class. Use runnables as your restartable tasks.
instanceofTom
@Curd - have you profiled your application and found that the cost of object allocation and thread GC makes up a non-negligible runtime cost? Unless you're doing something *insane*, it almost certainly won't. And in this case, just send `Runnable`s to a thread pool (such as `ThreadPoolExecutor`).
Andrzej Doyle
+1  A: 

Java Threads follow a lifecycle based on the State Diagram below. Once the thread is in a final state, it is over. That is simply the design.
alt text

akf
That's clear, but what are the reasons it was designed that way?
Curd
@Curd - see my response.
luis.espinal
Nice diagram :-). Still, it's true that this does not answer the question...
sleske
+5  A: 

I know that it is not possible to restart a used Java Thread object, but I don't find an explanation why this is not allowed; even if it is guaranteed that the thread has finished (see example code below).

My guestimation is that Threads might be directly tied (for efficiency or other constrains) to actual native resources that might be re-startable in some operating systems, but not in others. If the designers of the Java language had allowed Threads to be re-started, they might limit the number of operating systems on which the JVM can run.

Come to think of it, I cannot think of a OS that allows a thread or process to be restarted once it is finished or terminated. When a process completes, it dies. You want another one, you restart it. You never resurrect it.

Beyond the issues of efficiency and limitations imposed by the underlying OS, there is the issue of analysis and reasoning. You can reason about concurrency when things are either immutable or have a discrete, finite life-time. Just like state machines, they have to have a terminal state. Is it started, waiting, finished? Things like that cannot be easily reasoned about if you allow Threads to resurrect.

You also have to consider the implications of resurrecting a thread. Recreate its stack, its state, is is safe to resurrect? Can you resurrect a thread that ended abnormally? Etc.

Too hairy, too complex. All that for insignificant gains. Better to keep Threads as non-resurrectable resources.

luis.espinal
+1 The point about native resources and the stack issues sounds quite reasonable.
Curd
A: 

You can kind of get around this, either by using a java.util.concurrent.ThreadPoolExecutor, or manually by having a thread that calls Runnable.run() on each Runnable that it is given, not actually exiting when it is finished.

It's not exactly what you were asking about, but if you are worried about thread construction time then it can help solve that problem. Here's some example code for the manual method:

public class ReusableThread extends Thread {
    private Queue<Runnable> runnables = new LinkedList<Runnable>();
    private boolean running;

    public void run() {
        running = true;
        while (running) {
            Runnable r;
            try {
                synchronized (runnables) {
                    while (runnables.isEmpty()) runnables.wait();
                    r = runnables.poll();
                }
            }
            catch (InterruptedException ie) {
                // Ignore it
            }

            if (r != null) {
                r.run();
            }
        }
    }

    public void stopProcessing() {
        running = false;
        synchronized (runnables) {
            runnables.notify();
        }
    }

    public void addTask(Runnable r) {
        synchronized (runnables) {
            runnables.add(r);
            runnables.notify();
        }
    }
}

Obviously, this is just an example. It would need to have better error-handling code, and perhaps more tuning available.

Jonathan
A: 

If you are concerned with the overhead of creating a new Thread object then you can use executors.

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Testes {
    public static void main(String[] args) {
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(new Testes.A());
        executor.execute(new Testes.A());
        executor.execute(new Testes.A());
    }   
    public static class A implements Runnable{      
        public void run(){          
            System.out.println(Thread.currentThread().getId());
        }
    }
}

Running this you will see that the same thread is used for all Runnable objects.

gfelisberto