2009-07-31 18 views
26

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:Cola concurrente y de bloqueo en Java

  • Quiero el acceso simultáneo a la cola, el productor empujando, el receptor abierto.
  • Cuando la cola está vacía, quiero que el consumidor bloquee la cola, esperando al productor.

Mi primera idea fue utilizar un LinkedBlockingQueue, pero pronto me di cuenta de que no es concurrente y el rendimiento sufrió. Por otro lado, ahora uso un ConcurrentLinkedQueue, pero aún estoy pagando el costo de wait()/notify() en cada publicación. Dado que el consumidor, al encontrar una cola vacía, no bloquea, tengo que sincronizar y wait() en un bloqueo. Por otro lado, el productor tiene que 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 sea necesario.

Lo que supongo que es necesario aquí, es una cola que es a la vez de bloqueo y concurrente. Imagino una operación push() para trabajar como en ConcurrentLinkedQueue, con un notify() extra al objeto cuando el elemento empujado es el primero en la lista. Tal comprobación considero que ya existe en el ConcurrentLinkedQueue, ya que presionar 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?

+0

¿Por qué crees que java.util.concurrent.LinkedBlockingQueue no es concurrente? Creo que es perfectamente concurrente, al ver su javadoc y el código fuente. Pero no tengo idea sobre el rendimiento. – Rorick

+0

Ver también http://stackoverflow.com/questions/1301691/java-queue-implementations-which-one – Vadzim

Respuesta

11

Creo que puede atenerse a java.util.concurrent.LinkedBlockingQueue independientemente de sus dudas. Es concurrente. Sin embargo, no tengo idea de su rendimiento. Probablemente, otra implementación de BlockingQueue le conviene más. No hay muchos de ellos, por lo tanto, realice pruebas de rendimiento y mida.

+0

Digo esto solo al observar mi rendimiento reduciendo aproximadamente 8 veces en comparación con ConcurrentLinkedQueue. Supuse que estaba bloqueando todo internamente solo para ofrecer seguridad de hilo. Sin embargo, tienes razón, podría ser un desempeño peor en comparación con ConcurrentLinkedQueue. ¿Qué tipo de golpes la causa ;-) – Yiannis

+1

Cualquiera que sea la cola que utilice, va a terminar utilizando un bloqueo para apoyar la espera de una nueva entrada. (a menos que estés ocupado) El bloqueo realmente no es tan caro (alrededor de 0,5 microsegundos para Lock) por lo que si se trata de un problema de rendimiento, es posible que tengas un problema con tu diseño, p. cree menos tareas/encuentre una forma de agregar menos objetos a la cola/lote de su trabajo. –

+6

Como nota, si desea mayor rendimiento, use drainTo() en su LinkedBlcokingQueue, medimos casi 500% de rendimiento incrementado en una de nuestras aplicaciones, en lugar de tomar() 'uno y un elemento de la cola – nos

2

Aquí hay un list of classes implementing BlockingQueue.

Recomendaría visitar SynchronousQueue.

Como @Rorick mencionó en su comentario, creo que todas esas implementaciones son concurrentes. Creo que sus preocupaciones con LinkedBlockingQueue pueden estar fuera de lugar.

+1

SynchronousQueue no es lo que quiero, ya que bloqueará a mi productor cada vez que intente publicar. – Yiannis

+0

SynchronousQueue se parece más a una tubería que a una cola. Parece que no puede contener tareas pendientes de procesamiento en él, solo "empujar" tareas individuales de productor a consumidor. – Rorick

+0

Hehe, lo siento. – jjnguy

5

Sugeriría que miras en ThreadPoolExecutor newSingleThreadExecutor. Se encargará de mantener sus tareas ordenadas para usted, y si envía Callables a su ejecutor, podrá obtener el comportamiento de bloqueo que está buscando también.

3

Uso el ArrayBlockingQueue cada vez que necesito pasar datos de un hilo a otro. Usando los métodos de poner y tomar (que bloqueará si está lleno/vacío).

4

Usted puede tratar de LinkedTransferQueue jsr166: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

Cumple con los requisitos y tienen menos gastos para las operaciones de oferta/encuesta. Como puedo ver en el código, cuando la cola no está vacía, usa operaciones atómicas para los elementos de sondeo. Y cuando la cola está vacía, gira durante algún tiempo y estaciona el hilo si no tiene éxito. Creo que puede ayudar en su caso.

6

Similar a esta respuesta https://stackoverflow.com/a/1212515/1102730 pero un poco diferente ... Terminé usando un ExecutorService. Puede instanciar uno usando Executors.newSingleThreadExecutor(). Necesitaba una cola simultánea para leer/escribir imágenes Buffered a los archivos, así como la atomicidad con lecturas y escrituras. Solo necesito un hilo porque el archivo IO es en gran medida más rápido que el IO de origen. Además, estaba más preocupado por la atomicidad de las acciones y la corrección que por el rendimiento, pero este enfoque también se puede hacer con múltiples hilos en el conjunto 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-Finalmente 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 enjuagar y cerrar las transmisiones en un bloque final. No sé cómo funciona en comparación con otras soluciones, pero es bastante versátil.

Cuestiones relacionadas