Reputation: 1448
Please, look at this example. I take it from my production project. Webserver receive a command and starts new Thread which starts calculations via TheadPool. When user want to end calculations, he send another command which interrupts this new Thread, and workers of ThreadPool are shuting down. It's working fine, but I don't understand why.
public static void main(String[] args) throws Throwable {
final ExecutorService p = Executors.newFixedThreadPool(2);
System.out.println("main say: Hello, I'm Main!");
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " say: Starting monitor");
Thread monitor = new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
Thread.sleep(1500);
System.out.println(Thread.currentThread().getName() + " say: I'm still here...hahahahah");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " say: Bye for now!");
}
}
},"monitor");
monitor.setDaemon(true);
monitor.start();
List<Callable<Integer>> threads = new ArrayList<>();
for (int i = 0; i < 5; i++) {
threads.add(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + " say: Hello!");
try {
for (int c = 0; c < 5; c++) {
System.out.println(Thread.currentThread().getName() + " say: " + c);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " say: I'm interrupted :(");
}
System.out.println(Thread.currentThread().getName() + " say: Bye!");
return 0;
}
});
}
System.out.println(Thread.currentThread().getName() + " say: Starting workers");
try {
p.invokeAll(threads);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " say: I'm interrupted :(");
}
System.out.println(Thread.currentThread().getName() + " say: Bye!");
}
}, "new thread");
System.out.println("main say: Starting new thread");
t.start();
System.out.println("main say: Waiting a little...");
Thread.sleep(1250);
System.out.println("main say: Interrupting new thread");
t.interrupt();
// p.shutdown();
System.out.println(String.format("main say: Executor state: isShutdown: %s, isTerminated: %s",
p.isShutdown(),
p.isTerminated()));
System.out.println("main say: Bye...");
}
Main question: why does ThreadPool interrupts its workers, when currentThread interrupted? Where can I learn about this its behavior?
And why in this example main thread don't exits, but do nothing? ThreadPool is inactive but not isTerminated and isShutdown and don't processing rest of tasks.
Upvotes: 3
Views: 1554
Reputation: 9135
The interrupts to your tasks are mentioned in the API of ExecutorService.invokeAll()
:
Throws:
InterruptedException
- if interrupted while waiting, in which case unfinished tasks are cancelled
So when the interrupt is received during your call to p.invokeAll(threads)
, all the tasks in threads
are cancelled.
The API doesn't specify if Future.cancel()
is called with mayInterruptIfRunning
or not, but if you look in the code for AbstractExecutorService
, from which ThreadPoolExecutor
inherits its implementation of invokeAll()
, you can see that the tasks are cancelled with interrupts enabled:
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
/* ... */
try {
/* ... */
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
I suppose this makes slightly more sense than cancelling them without interrupts, because there's already been an interrupt; this is "just propagating it".
The program doesn't exit, and the thread pool is not shut down or terminated, because you simply never told it to shut it down.
So this is no different from the following reduced program:
public static void main(String[] args) throws Throwable {
final ExecutorService p = Executors.newFixedThreadPool(2);
p.execute(new Runnable() { public void run() { } });
Thread.sleep(1000);
System.out.println(String.format("main say: Executor state: isShutdown: %s, isTerminated: %s",
p.isShutdown(),
p.isTerminated()));
}
Thread pools don't have any special magic to guess when you meant to shut them down; they wait until you actually tell them to. The documentation for Executors.newFixedThreadPool()
states:
The threads in the pool will exist until it is explicitly
shutdown
.
When you create thread pools, you need to ensure that they're eventually cleaned up. Usually this is by calling shutdown()
or shutdownNow()
. Why is this necessary? Because running threads are special in the context of Java garbage collection. Running threads are the starting points for determining what objects will not be garbage collected, and will never be garbage collected while they are still running. And a Java program never exits while there are still running threads (unless you call System.exit()
, of course.)
There are some special situations where a thread pool might have no running threads, and thus be garbage collected. The API docs for ThreadPoolExecutor
explains this:
Finalization
A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call
shutdown()
, then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or settingallowCoreThreadTimeOut(boolean)
.
So we can modify my example above to eventually exit like this:
final ThreadPoolExecutor p = new ThreadPoolExecutor(
0, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
or this:
final ThreadPoolExecutor p = new ThreadPoolExecutor(
2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
p.allowCoreThreadTimeOut(true);
But it's often cleaner to call shutdown
or shutdownNow
when you're finished with your thread pool, instead of relying on a timeout.
Upvotes: 0
Reputation: 180201
Main question: why does ThreadPool interrupts its workers, when currentThread interrupted? Where can I learn about this its behavior?
You are overgeneralizing. The invokeAll()
methods of an ExecutorService
cancel all unfinished tasks when they are interrupted. This is documented in the API docs.
If you're asking "how would I know it will do that" then the docs are your answer. If you're asking why the interface is designed that way, then it makes sense because when it is interrupted, the method throws InterruptedException
instead of returning a value, and therefore it is reasonable to suppose that any further work that those unfinished tasks might perform would be wasted.
And why in this example main thread don't exits, but do nothing?
The "main thread" is the one that started at the beginning of main()
. This thread does exit, and before it does so it does several other things, including creating, starting, and interrupting a Thread, and outputting several messages. It exits when control reaches the end of main()
.
But perhaps you mean thread "new thread" started directly by the main thread. This thread also does several things, including starting the monitor thread and submitting a job to the executor service. Or maybe you're asking why this thread does not exit while the ExecutorService
is working on its job, but why would it exit while it's waiting for the invokeAll()
method to return? Even though that method returns a list of Future
s, its documentation is clear that it blocks until all the tasks submitted to it are complete, or an exception occurs.
Upvotes: 2