arrayblockingqueue - blockingqueue java 8
Cola concurrente y de bloqueo en Java (6)
Tengo el problema clásico de un hilo que empuja eventos a la cola entrante de un segundo hilo. Solo que esta vez, estoy muy interesado en el rendimiento. Lo que quiero lograr es:
- Quiero acceso simultáneo a la cola, al productor presionando, al receptor emergente.
- Cuando la cola está vacía, quiero que el consumidor bloquee la cola, esperando al productor.
Mi primera idea fue usar un LinkedBlockingQueue
, pero pronto me di cuenta de que no es concurrente y que el rendimiento ha sufrido. Por otro lado, ahora uso un ConcurrentLinkedQueue
, pero todavía pago el costo de wait()
/ notify()
en cada publicación. Dado que el consumidor, al encontrar una cola vacía, no se bloquea, tengo que sincronizar y wait()
en un bloqueo. Por otra parte, el productor debe obtener ese bloqueo y notify()
en cada publicación. El resultado general es que estoy pagando el costo de sycnhronized (lock) {lock.notify()}
en cada publicación, incluso cuando no es necesario.
Lo que supongo que se necesita aquí, es una cola que es tanto de bloqueo como concurrente. Imagino una operación push()
para funcionar como en ConcurrentLinkedQueue
, con una notify()
adicional notify()
al objeto cuando el elemento push()
es el primero en la lista. Tal comprobación considero que ya existe en el ConcurrentLinkedQueue
, ya que empujar requiere conectarse con el siguiente elemento. Por lo tanto, esto sería mucho más rápido que sincronizar cada vez en el bloqueo externo.
¿Hay algo como esto disponible / razonable?
Aquí hay una lista de clases que implementan BlockingQueue
.
Recomendaría revisar SynchronousQueue
.
Como mencionó @Rorick en su comentario, creo que todas esas implementaciones son concurrentes. Creo que sus preocupaciones con LinkedBlockingQueue
pueden estar fuera de lugar.
Creo que puedes apegarte a java.util.concurrent.LinkedBlockingQueue
sin importar tus dudas. Es concurrente. Sin embargo, no tengo idea de su rendimiento. Probablemente, otra implementación de BlockingQueue
te será mejor. No hay demasiados de ellos, así que haga pruebas de rendimiento y mida.
Puede probar LinkedTransferQueue desde jsr166: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
Cumple con sus requisitos y tiene menos gastos generales para las operaciones de oferta / sondeo. Como puedo ver en el código, cuando la cola no está vacía, utiliza operaciones atómicas para los elementos de sondeo. Y cuando la cola está vacía, gira por un tiempo y aparca el hilo si no tiene éxito. Creo que puede ayudar en tu caso.
Similar a esta respuesta https://.com/a/1212515/1102730 pero un poco diferente .. Terminé usando un ExecutorService
. Puede crear una instancia utilizando Executors.newSingleThreadExecutor()
. Necesitaba una cola concurrente para leer / escribir BufferedImages en archivos, así como atomicidad con lecturas y escrituras. Solo necesito un solo hilo porque el archivo IO es órdenes de magnitud más rápido que la fuente, net IO. Además, me preocupaba más la atomicidad de las acciones y la corrección que el rendimiento, pero este enfoque también se puede hacer con varios subprocesos en el grupo para acelerar las cosas.
Para obtener una imagen (Try-Catch-Finally omitido):
Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
@Override
public BufferedImage call() throws Exception {
ImageInputStream is = new FileImageInputStream(file);
return ImageIO.read(is);
}
})
image = futureImage.get();
Para guardar una imagen (Try-Catch-Finally omitido):
Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() {
FileOutputStream os = new FileOutputStream(file);
return ImageIO.write(image, getFileFormat(), os);
}
});
boolean wasWritten = futureWrite.get();
Es importante tener en cuenta que debe vaciar y cerrar sus transmisiones en un bloque final. No sé cómo funciona en comparación con otras soluciones, pero es bastante versátil.
Te sugiero que mires ThreadPoolExecutor newSingleThreadExecutor. Se encargará de mantener sus tareas ordenadas para usted, y si envía Callables a su ejecutor, también podrá obtener el comportamiento de bloqueo que está buscando.
Uso el ArrayBlockingQueue cuando necesito pasar datos de un hilo a otro. Usando los métodos de poner y tomar (que se bloquearán si están llenos / vacíos).