Java executorservice invokeall() interrupt
I have a fixed thread pool executorservice with a width of 10 and a 100 callable list, each waiting for 20 seconds and recording their interrupts
I invoke the invokeAll in the list in a single thread and interrupt the thread almost immediately. Executorservice execution was interrupted as expected, but the actual number of interrupts recorded by callables was much higher than expected 10 – about 20-40 Why, if executorservice can execute no more than 10 threads at the same time?
Full source: (you may need to run it once due to concurrency)
@Test public void interrupt3() throws Exception{ int callableNum = 100; int executorThreadNum = 10; final AtomicInteger interruptCounter = new AtomicInteger(0); final ExecutorService executorService = Executors.newFixedThreadPool(executorThreadNum); final List <Callable <Object>> executeds = new ArrayList <Callable <Object>>(); for (int i = 0; i < callableNum; ++i) { executeds.add(new Waiter(interruptCounter)); } Thread watcher = new Thread(new Runnable() { @Override public void run(){ try { executorService.invokeAll(executeds); } catch(InterruptedException ex) { // NOOP } } }); watcher.start(); Thread.sleep(200); watcher.interrupt(); Thread.sleep(200); assertEquals(10,interruptCounter.get()); } // This class just waits for 20 seconds,recording it's interrupts private class Waiter implements Callable <Object> { private AtomicInteger interruptCounter; public Waiter(AtomicInteger interruptCounter){ this.interruptCounter = interruptCounter; } @Override public Object call() throws Exception{ try { Thread.sleep(20000); } catch(InterruptedException ex) { interruptCounter.getAndIncrement(); } return null; } }
Using WinXP 32-bit, Oracle JRE 1.6 0_ 27 and junit4
Solution
I don't agree with the assumption that you should only receive 10 interrupts
Assume the cpu has 1 core. 1. Main thread starts Watcher and sleeps 2. Watcher starts and adds 100 Waiters then blocks 3. Waiter 1-10 start and sleep in sequence 4. Main wakes and interrupts Watcher then sleeps 5. Watcher cancels Waiter 1-5 then is yielded by the OS (Now we have 5 interrupts) 6. Waiter 11-13 start and sleep 7. Watcher cancels Waiter 6-20 then is yielded by the OS (Now we have 13 interrupts) 8. Waiter 14-20 are "started" resulting in a no-op 9. Waiter 21-24 start and sleep ....
In essence, my argument is that there is no guarantee that the watcher thread will allow all 100 "waiter" runnablefuture instances to be cancelled before allowing time slices to be generated and allowing the worker thread of executorservice to start more waiter tasks
Update: displays the code from abstractexecutorservice
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null) throw new NullPointerException(); List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { for (Callable<T> t : tasks) { RunnableFuture<T> f = newTaskFor(t); futures.add(f); execute(f); } for (Future<T> f : futures) { if (!f.isDone()) { try { f.get(); //If interrupted,this is where the InterruptedException will be thrown from } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } } done = true; return futures; } finally { if (!done) for (Future<T> f : futures) f.cancel(true); //Specifying "true" is what allows an interrupt to be sent to the ExecutorService's worker threads } }
The finally block containing F. cancel (true) is when an interrupt is propagated to the currently running task As you can see, this is a compact loop, but there is no guarantee that the thread executing the loop can traverse all instances of future in a time slice