views:

348

answers:

5
+3  Q: 

java thread reuse

I have always read that creating threads is expensive. I also know that you cannot rerun a thread.

I see in the doc of Executors class: Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.

Mind the word 'reuse'.

How do thread pools 'reuse' threads?

A: 

Creating threads is actually pretty cheap on Windows. Creating processes is more expensive on Windows and cheeper on *nix.

To reuse a thread, you block it on a mutex. Then, when there is work to do, you set the next instruction to be the work in question, and unblock it.

Think of running the function you call with the thread inside a loop. When there is work to do, it is called with the parameters passed in. When there is no work to do, it is parked for free.

Jacob

TheJacobTaylor
@TheJacobTaylor: the question is about Java threads. And Java doesn't have "functions" but "methods".
Webinator
@WizardOfOdds: I was attempting to give a generic answer and was using the word function for a generic piece of functionality. You are correct. In the Java universe I should have used the word method. Since Java does not have "functions" I hope I have not confused too many people.
TheJacobTaylor
@WizardOfOdds: BTW thank you for telling me why you were taking points from my answer. I hate not knowing.
TheJacobTaylor
+2  A: 

The thread pool consists of a number of fixed worker threads that can take tasks from an internal task queue. So if one task ends, the thread does not end but waits for the next task. If you abort a thread, it is automatically replaced.

Look at the documentation for more details.

AndiDog
A: 

If the thread is finished, sure one can reuse it again. You can make sure it is not running by calling isAlive() or something similar.

EDIT : If someone has suspended a thread, one cannot start again. I don't why it cannot be started if it has finished normally. But I give the benefit of doubt and say it cannot be reused.

fastcodejava
@fastcodejava: If a Thread is finished you cannot call *start()* again, that would give you an *IllegalThreadStateException*.
Webinator
A: 

The run method of threads in a thread pool does not consist only of running a single task. The run method of a thread in a thread pool contains a loop. It pulls a task off of a queue, executes the task (which returns back to the loop when it is complete), and then gets the next task. The run method doesn't complete until the thread is no longer needed.

Edited to add:

Here is the run method of the Worker inner class in ThreadPoolExecutor.

696:         /**
697:          * Main run loop
698:          */
699:         public void run() {
700:             try {
701:                 Runnable task = firstTask;
702:                 firstTask = null;
703:                 while (task != null || (task = getTask()) != null) {
704:                     runTask(task);
705:                     task = null; // unnecessary but can help GC
706:                 }
707:             } finally {
708:                 workerDone(this);
709:             }
710:         }
Mike Daniels
@Mike Daniels: exactly... That said, you gotta love that Sun code that assigns *"task = null;"* and then comments *"// unnecessary but can help GC"*. You'd post a piece of code here on SO doing that and you'd get jump on your throat by groupthinkers telling you how pointless it is ;)
Webinator
@Wizard: It's not actually unnecessary - the (strange) test for task!=null in the loop makes it necessary to prevent continually processing the same task. Even if the loop were more conventional nulling task would be good because otherwise if getTask() blocks for a long time, the GC of task would otherwise be delayed for the same length of time.
Software Monkey
@Software Monkey: very insightful about the second point. `getTask` is probably pulling out of a blocking queue with a timeout. That might block for the full length of the timeout. If the task has a lot of fields (for passed-in parameters to the task), earlier GC would be helpful.
Mike Daniels
+2  A: 

I think I understood what is confuzzabling you so here's my longer answer: the terminology is a tiny bit misleading (obviously, or you wouldn't ask that question specifically putting the emphasis on 'reuse'):

How do thread pools 'reuse' threads?

What is happening is that a single thread can be used to process several tasks (typically passed as Runnable, but this depend on your 'executor' framework: the default executors accepts Runnable, but you could write your own "executor" / thread-pool accepting something more complex than a Runnable [like, say, a CancellableRunnable]).

Now in the default ExecutorService implementation if a thread is somehow terminated while still in use, it is automatically replaced with a new thread, but this is not the 'reuse' they're talking about. There is no "reuse" in this case.

So it is true that you cannot call start() on a Java Thread twice but you can pass as many Runnable as you want to an executor and each Runnable's run() method shall be called once.

You can pass 30 Runnable to 5 Java Thread and each worker thread may be calling, for example, run() 6 times (practically there's not guarantee that you'll be executing exactly 6 Runnable per Thread but that is a detail).

In this example start() would have been called 6 times. Each one these 6 start() will call exactly once the run() method of each Thread:

From Thread.start() Javadoc:

 * Causes this thread to begin execution; the Java Virtual Machine 
 * calls the <code>run</code> method of this thread.

BUT then inside each Thread's run() method Runnable shall be dequeued and the run() method of each Runnable is going to be called. So each thread can process several Runnable. That's what they refer to by "thread reuse".

One way to do your own thread pool is to use a blocking queue on to which you enqueue runnables and have each of your thread, once it's done processing the run() method of a Runnable, dequeue the next Runnable (or block) and run its run() method, then rinse and repeat.

I guess part of the confusion (and it is a bit confusing) comes from the fact that a Thread takes a Runnable and upon calling start() the Runnable 's run() method is called while the default thread pools also take Runnable.

Webinator