Something strange in implementing the BoundedExecutor in the Java Concurrency book in practice.
He must submit the subordination of the task to the Contractor, blocking the feed flow when the Contractor has enough threads in the queue or working.
This is the implementation (after adding the missing retrow to the catch clause):
public class BoundedExecutor { private final Executor exec; private final Semaphore semaphore; public BoundedExecutor(Executor exec, int bound) { this.exec = exec; this.semaphore = new Semaphore(bound); } public void submitTask(final Runnable command) throws InterruptedException, RejectedExecutionException { semaphore.acquire(); try { exec.execute(new Runnable() { @Override public void run() { try { command.run(); } finally { semaphore.release(); } } }); } catch (RejectedExecutionException e) { semaphore.release(); throw e; } }
When I create an instance of BoundedExecutor using Executors.newCachedThreadPool() and is associated with the number 4, I expect that the number of threads created by the cached thread pool will never exceed 4. However, in practice this happens. I got this little test program to create as many as 11 threads:
public static void main(String[] args) throws Exception { class CountingThreadFactory implements ThreadFactory { int count; @Override public Thread newThread(Runnable r) { ++count; return new Thread(r); } } List<Integer> counts = new ArrayList<Integer>(); for (int n = 0; n < 100; ++n) { CountingThreadFactory countingThreadFactory = new CountingThreadFactory(); ExecutorService exec = Executors.newCachedThreadPool(countingThreadFactory); try { BoundedExecutor be = new BoundedExecutor(exec, 4); for (int i = 0; i < 20000; ++i) { be.submitTask(new Runnable() { @Override public void run() {} }); } } finally { exec.shutdown(); } counts.add(countingThreadFactory.count); } System.out.println(Collections.max(counts)); }
I think that there is a short period of time between the release of the semaphore and the completion of the task, when another thread can get permission and send the task until the release thread is not finished yet. In other words, he has a race condition.
Can someone confirm this?
java concurrency race-condition executor
Jan van den bosch
source share