I've looked around but haven't found an answer so I wanted to confirm this for certain.
Say I have a fixed size thread pool - ExecutorService pool = Executors.newFixedThreadPool(5);
And I have some code:
pool.execute(new Runnable(){
try{
Object waitForMe = doSomethingAndGetObjectToWaitFor();
waitForMe.wait();
doSomethingElse();
}catch(Exception e){ throw new RunTimeException(e) }
});
Lets assume that the above code is called a few 100 times. There are only 5 threads in the pool (so only 5 of the above statements should be live at one point). Also assume that the wait()
is on an object doing some I/O calls to a thrid party and waiting for a callback when the operation is complete so it will naturally take a while to complete.
Now my question is what is the behavior when one of these tasks reaches a wait()
, does the task go to sleep and then the thread from the thread pool takes another task off queue and starts running it?
If the task that is waiting goes to sleep what happens when it gets a notify()
and wakes up? Does the thread go back into the queue (at the front or back) for the thread pool and wait until one of the 5 threads can continue to execute it (i.e. call doSomethingelse()
)? Or does the thread that was executing it also go to sleep i.e. one of the 5 executor threads sits waiting with the task (this is what I'm assuming)? Or does the executor thread pick up another task and simply get interrupted when the first task returns from the wait()?
ThreadPoolExecutor is an ExecutorService to execute each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods. It also provides various utility methods to check current threads statistics and control them.
Once a thread in the thread pool completes its task, it's returned to a queue of waiting threads. From this moment it can be reused. This reuse enables applications to avoid the cost of creating a new thread for each task.
Executors framework helps you with - Thread Creation: It provides various methods for creating threads, more specifically a pool of threads, that your application can use to run tasks concurrently. Thread Management: It manages the life cycle of the threads in the thread pool.
If additional tasks are submitted when all threads are active, they will wait in the queue until a thread becomes available. If any thread terminates due to failure during execution, it will be replaced by a new one. The threads in the pool will exist until it is explicitly shutdown.
wait()
is a blocking operation:
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll()
This means that the thread in the pool will wait, but from outside it just looks like the current task takes so much time to complete. This also means that if 5 tasks are executed and they all wait()
, the Executor
cannot handle remaining tasks that, ekhem, wait in the queue.
True, the executor thread itself goes to sleep allowing other threads to switch and consume CPU (so you can have hundreds of threads waiting at the same time and your system is still responsive) but still the thread is "unusable" and blocked.
Another interesting feature is interrupting - if the thread waits for something or sleeps you can interrupt it. Note that both wait()
and Thread.sleep()
declare InterruptedException
. With ExecutorService
you can take advantage of this by simply calling: future.cancel()
(future
is the object you got in return when submit task to ExecutorService
).
Finally I think you should redesign your solution. Instead of actively waiting for an external system to finish, provide an API with callbacks:
pool.execute(new Runnable(){
try{
doSomethingAndCallMeBackWhenItsDone(new Callback() {
public void done() {
doSomethingElse();
}
});
}catch(Exception e){ throw new RunTimeException(e) }
});
This way the external system's API will simply notify you when the results are ready and you won't have to wait and block ExecutorService
. Finally, if doSomethingElse()
takes a lot of time, you might even decide to schedule it as well rather than using external third-party I/O thread:
pool.execute(new Runnable(){
try{
doSomethingAndCallMeBackWhenItIsDone(new Callback() {
public void done() {
pool.submit(new Callbale<Void>() {
public Void call() {
doSomethingElse();
}
}
}
});
}catch(Exception e){ throw new RunTimeException(e) }
});
UPDATE: you are asking what to do about timeouts? Here is my idea:
pool.execute(new Runnable(){
try{
doSomethingAndCallMeBackWhenItsDone(new Callback() {
public void done() {
doSomethingElse();
}
public void timeout() {
//opps!
}
});
}catch(Exception e){ throw new RunTimeException(e) }
});
I guess you can implement timeout on the third-party side and if timeout occurs there, simply call timeout()
method.
The wait()
cannot know anything about the tread pool. And the thread pool cannot know anything about the wait()
. So they cannot interact anyhow.
They work as usual - the wait()
just is a long running blocking operation, the thread pool is just a queue of runnables to run on limited pool of threads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With