java thread reusage via executor
Asked Answered
P

3

7

I am confused on the following:
To use threads in a Java program, the simplest way is to extend Thread class and implement the runnable interface (or simply implement runnable).
To start the thread's execution. we must call the Thread's method start(), which in turn calls method run() of the thread. And so the thread starts.
The method start() (unless I am wrong) must be called exactly and only once for each thread. As a result, thread instances can not be reused unless somehow the run method itself runs in some-short of infinite loop that facilitates a custom implementation of the thread's reusage.
Now the javadoc link text says

Calls to execute will reuse previously constructed threads if available

I do not understand how this is implemented. I provide in the execute method of the executor method my custom thread e.g.

  ExecutorService myCachedPool = Executors.newCachedThreadPool();
  myCachedPool.execute(new Runnable(){public void run(){  
     //do something time consuming

  }});

How can this custom thread I delegeate to the executor framework be reused?
Is Executor is allowed to call method start() more than 1 time, while we can not in our programs? Am I misunderstanding something?

Thank you.

Peevish answered 19/9, 2010 at 10:48 Comment(0)
B
5

Note that it's not Executor that calls start() - it's ExecutorService. And no, it's not calling start() twice. It doesn't start the task that you give it directly using Thread.start()... instead, it starts a thread which knows about that thread pool's queue of work. The thread will basically wait until there's some work to do, then pick it up and execute it, before going back to waiting. So although the thread performs several tasks, Thread.start() is only called once.

EDIT: Judging by the comments, you're a bit confused about the difference between a Runnable (which is a task to be executed) and a Thread (which is what executes tasks).

The same thread can execute multiple tasks. For a very simple example not using a thread pool, consider this:

public class MultiRunnable implements Runnable
{
    private final List<Runnable> runnables;

    public MultiRunnable(List<Runnable> runnables)
    {
        this.runnables = runnables;
    }

    public void run()
    {
        for (Runnable runnable : runnables)
        {
             runnable.run();
        }
    }
}

(Ignore the potential thread safety issues of using a List<T> from multiple threads.)

You could create a whole bunch of Runnable tasks capable of doing different things, then create a single MultiRunnable to run them in turn. Pass that instance of MultiRunnable into the Thread constructor, and then when you start the thread, it will execute each of the original runnable tasks. Does that help?

Bugeye answered 19/9, 2010 at 11:23 Comment(7)
@Jon: Sorry I lost you. So the internal threads of the framework get reused and not the runnable I pass as argument to execute? My thread I delegate to the framework will be re-instantiated each time, but by the same instance of the internal thread created by the framework?Peevish
@user384706: Yes. Your runnable isn't a thread - it's just a task to be executed. You need to differentiate between the two; they're very different.Bugeye
@Jon: Thank you very much. The only thing I am not sure is, what is the benefit of using the Executors.newCachedThreadPool(); Because if my class that implements runnable (for a task) is expensive to be instantiated, it will not be reused and the same thread of the framework, will keep using new instances of the task. So what would I gain from this api? Unless the concept is that each class that implements runnable is minimal.Peevish
@user384706 Creating a thread has a relativly large overhead, and there's nothing you can do about that. Creating an instance of a class is very cheap in comparison (and if you made it expensive, e.g. it's hitting a database/reading files etc. in its constructor, you can refactor that, unlike threads whose overhead you can't control)Zonate
@nos: So my tasks should not be extending Thread but just implement runnable? Because if the do extend Thread, the "relatively large overhead" of thread creation is still there. Right?Peevish
Thank you, this is really helpful. But one follow up question - How will GC happen in such case, if the Thread is never completed, will all Tasks passed to a thread will remain in memory even if they have completed their individual execution. Will the GC only happen after the Thread is somehow destoryed?Philippi
@YashAgarwal: I think it would be best to ask a new question with a concrete example of your exact situation.Bugeye
S
5

It is not calling start() more than once; instead the Thread in the pool never completes, but just stays alive---waiting. The source code is available for download if you want to look at it.

Each Thread in the thread pool can simply wait() for the Executor to hand it a new Runnable, but the Thread's own run() method has not completed. It simply waits for a new Runnable to be given to the Executor.

Sporocarp answered 19/9, 2010 at 11:24 Comment(2)
So you mean that in the javadoc where it says "that previously constructed threads will be reused" it does not refer to the runnable I pass to the execute, but internal threads of the executors framework? So my Runnable will be re-instantiated by the same Thread of thread pool? Have I understood what you are saying?Peevish
Your Runnable won't be reinstantiated. Your Runnable instance will be given to one of the Threads in the pool. That thread has its own run() method, which is not (cannot be) replaced by your Runnable's run() method. The Thread's run() method will call your Runnable's run() method, and then after your Runnable.run() finishes, the thread will eventually (there's some bookkeeping that would have to be done) go back to waiting.Sporocarp
W
1

To "start" a thread more than once, create a runnable. For example:

//NO
private class T extends Thread { //not necessary to implement runnable
    public void run(){
        //...
    }
}
void someMethod(){
    T a = new T();
    a.start();
    a.start(); //NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO
}

Instead,

//Yes
private class T implements Runnable {
    public void run(){
        //...
    }
}
void someMethod(){
    T a = new T();
    new Thread(a).start();
    new Thread(a).start(); //YES YES YES
}

It is also possible to do this:

void someMethod(){
    final Runnable r = new Runnable(){
        public void run(){
            //...
        }
    };
    new Thread(r).start();
    new Thread(r).start();
}
// r could also be a field of you class. 
Worsen answered 19/9, 2010 at 13:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.