Параллелизм в Java на практике:Состояние гонки в BoundedExecutor?

Есть что-то странное в реализации BoundedExecutorв книге Java Concurrency in Practice.

Предполагается, что передача задачи исполнителю будет регулироваться путем блокировки отправляющего потока, когда имеется достаточное количество потоков, либо поставленных в очередь, либо работающих в исполнителе.

Это реализация (после добавления отсутствующего повторного вызова в предложении catch):

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;
        }
    }

Когда я создаю экземпляр BoundedExecutorс Executors.newCachedThreadPool()и границей 4, я ожидаю, что количество потоков, созданных экземпляром кешированный пул потоков никогда не превышал 4. На практике, однако, это так. Я получил эту маленькую тестовую программу для создания целых 11 потоков :

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));
}

. Я думаю, что между выпуском семафора и завершением задачи есть крошечный промежуток времени, когда другой поток может получить разрешение и отправить задачу, в то время как освобождение потока еще не завершено. Другими словами, у него есть состояние гонки.

Кто-нибудь может это подтвердить?

8
задан Jan Van den bosch 10 April 2012 в 18:04
поделиться