java - new - Implementación elegante de indicadores de longitud de cola para servicios de ejecución
new icon java (2)
¿Por qué, oh por qué java.util.concurrent
proporciona indicadores de longitud de cola para sus Servicios de ExecutorService
? Recientemente me encontré haciendo algo como esto:
ExecutorService queue = Executors.newSingleThreadExecutor();
AtomicInteger queueLength = new AtomicInteger();
...
public void addTaskToQueue(Runnable runnable) {
if (queueLength.get() < MAX_QUEUE_LENGTH) {
queueLength.incrementAndGet(); // Increment queue when submitting task.
queue.submit(new Runnable() {
public void run() {
runnable.run();
queueLength.decrementAndGet(); // Decrement queue when task done.
}
});
} else {
// Trigger error: too long queue
}
}
Lo cual funciona bien, pero ... Creo que esto debería implementarse como parte del Servicio de ExecutorService
. Es tonto y propenso a errores transportar un contador separado de la cola real, cuya longitud se supone que debe indicar el contador (me recuerda a los arrays C). Sin embargo, los servicios de ExecutorService
se obtienen a través de métodos de fábrica estáticos, por lo que no hay manera de simplemente extender el ejecutor de un solo hilo excelente y agregar un contador de cola. Entonces qué debo hacer:
- Reinventar cosas ya implementadas en JDK?
- ¿Otra solución inteligente?
Hay una forma más directa:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newSingleThreadExecutor();
// add jobs
// ...
int size = executor.getQueue().size();
Aunque podría considerar no utilizar los métodos de creación de Ejecutor convenientes, sino crear el ejecutor directamente para deshacerse de la conversión y, por lo tanto, asegurarse de que el ejecutor siempre será realmente ThreadPoolExecutor
, incluso si la implementación de Executors.newSingleThreadExecutor
cambiaría algún día.
ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() );
Esto se copia directamente de Executors.newSingleThreadExecutor
en JDK 1.6. El LinkedBlockingQueue
que se pasa al constructor es en realidad el objeto que obtendrá de getQueue
.
Mientras que puede comprobar el tamaño de la cola directamente. Otra forma de tratar con una cola que se está volviendo demasiado larga es haciendo que la cola interna esté delimitada.
public static
ExecutorService newFixedThreadPoolWithQueueSize(int nThreads, int queueSize) {
return new ThreadPoolExecutor(nThreads, nThreads,
5000L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(queueSize, true));
}
Esto causará RejectedExecutionExceptions (vea this ) cuando exceda el límite.
Si desea evitar la excepción, el subproceso de llamada puede ser secuestrado para ejecutar la función. Vea esta pregunta SO para una solución.