Es gibt etwas Seltsames an der Implementierung des BoundedExecutor
in dem Buch Java Concurrency in Practice.Java Concurrency in der Praxis: Race-Bedingung in BoundedExecutor?
Es soll die Aufgabenübergabe an den Executor abbremsen, indem der übergebende Thread blockiert wird, wenn genügend Threads entweder in der Warteschlange stehen oder im Executor ausgeführt werden.
Dies ist die Implementierung (nach dem fehlenden rethrow in der catch-Klausel hinzufügen):
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;
}
}
Als ich die BoundedExecutor
mit einem Executors.newCachedThreadPool()
und einer Grenze von 4 instanziiert, würde ich die Anzahl der Threads erwartet instanziiert durch der Cache-Thread-Pool sollte nie größer als 4 sein. In der Praxis ist dies jedoch der Fall. Ich habe dieses kleine Testprogramm bekommen, so viel wie 11 Threads zu erstellen:
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));
}
Ich denke, es ist ein winzig kleiner Zeitrahmen zwischen der Freigabe des Semaphors und die Aufgabe endet, wo ein anderer Thread eine Genehmigung erwerben kann und Aufgabe einreichen, solange der Freigabe-Thread noch nicht beendet ist. Mit anderen Worten, es hat eine Wettlaufbedingung.
Kann jemand das bestätigen?
Ich fügte ein 1ms Thread.sleep direkt nach dem semaphore.release() hinzu, um zu sehen, wie viel schlimmer es würde: Ich habe über 300 Threads erstellt. – toto2