《 Java并发实践》一书中BoundedExecutor
的实现有些奇怪。
当在执行器中有足够的线程排队或正在运行时,应该通过阻止提交线程来限制向执行器提交任务。
这是实现(在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
和4的边界实例化Executors.newCachedThreadPool()
时,我期望由缓存线程池实例化的线程数永远不会超过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));
}
我认为,在信号量释放和任务结束之间有一个很小的时间范围,其中另一个线程可以在释放线程尚未完成的情况下获得许可并提交任务。换句话说,它具有竞争条件。
有人可以确认吗?
最佳答案
我看到一次创建了多达9个线程。我怀疑存在竞争状况,导致线程数量超出了要求。
这可能是因为在运行任务之前和之后都有要完成的工作。这意味着即使您的代码块中只有4个线程,也有许多线程停止了先前的任务或准备开始新的任务。
即线程在仍在运行时执行release()。即使这是最后一件事,您也不是在获得新任务之前要做的最后一件事。