2012-07-22 15 views
12

He pasado por un conjunto de sorpresas cuando se trata de la implementación de cola para un sistema de subprocesamiento múltiple. Aquí está: -Sincronizado vs ReentrantLock en el rendimiento

El escenario: - 1 productor, 1 consumidor: - Un productor pone un número entero en una cola. Un consumidor simplemente lo elimina de la cola.

La estructura de datos subyacente de la cola: - TreeSet (que nunca pensé que voy a utilizar), LinkedList, LinkedBlockingQueue (con un tamaño indefinido)

El código: - de TreeSet como una cola: -

while (i < 2000000) { 
     synchronized (objQueue) { 

      if (!(objQueue.size() > 0)) { 
       try { 
        objQueue.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
      Integer x = objQueue.first(); 
      if (x != null) { 
       objQueue.remove(x); 
       ++i; 
      } 
     } 
    } 

EDIT: -

 while (i < 2000000) { 
     synchronized (objQueue) { 
      objQueue.add(i); 
      ++i; 
      objQueue.notify(); 
     } 
    } 

Para LinkedBlockingQueue: -

 while (i < 2000000){ 
     try { 
      objQueue.put(i); 
      ++i; 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      Thread.currentThread().interrupt(); 
     } 
    } 

     while (i < 2000000) { 
     try { 
      objQueue.take(); 
      ++i; 

     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      Thread.currentThread().interrupt(); 
     } 
    } 

Para LinkedList: código similar con sincronizado.

las preguntas: -

1) Cuando he medido el rendimiento a través de Visual VM, he observado que la del código del productor, TreeSet se comporta mejor que LinkedBlockingQueue y LinkedList, a pesar de que toma O (log el tiempo n) , la creación de objetos en estructuras vinculadas es una sobrecarga significativa. ¿Por qué la teoría es bastante diferente de la práctica? ¿Por qué preferimos estructuras vinculadas y de matriz sobre estructuras de árbol en implementaciones de cola?

2) El sincronizado aparece como un ganador claro frente a ReeentrantLock porque TreeSet funcionó mejor que LinkedList, que tuvo un mejor rendimiento que LinkedBlockingQueue. Desearía poder adjuntar los resultados de Visual VM. No es en votos con el artículo, http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html

Las operaciones se realizan en

Dell Vostro 1015, core 2 duo 2,10, 2 GB Ram con el sistema operativo de 32 bits y con

JVM: Java HotSpot (TM) VM cliente (20.1-b02, modo mixto) Java: la versión 1.6.0_26, proveedor de Sun Microsystems Inc.

Respuesta

13

1.ReentrantLock podría ser más propensos a utilizar si es necesario implementar un hilo que atraviesa una lista enlazada, bloquea el siguiente nodo y luego desbloquea el nodo actual.

2.Synchronized palabra clave es apto en situación como engrosamiento de bloqueo, proporciona hilado de adaptación, de bloqueo parcial y el potencial de elision de bloqueo a través de análisis de escape. Esas optimizaciones no están implementadas actualmente para ReentrantLock.

Para una comparación de rendimiento adecuado ver esto:

http://lycog.com/concurency/performance-reentrantlock-synchronized/

+0

Es casi el mismo ejemplo para el que adjunté un enlace en mi pregunta. Me dice que el rendimiento de REL es mejor que sincronizado comenzando con 2 hilos. También creo que si obtengo una manera de forzar al programador de sistema operativo a ejecutar los hilos en todas las CPU disponibles, definitivamente obtendremos un mejor rendimiento para LinkedBlockingQueues. Si necesita mis datos de observación, hágamelo saber. – 100pipers

+2

Kumar olvidó mencionar que tomó su respuesta de [David Dice's Weblog] (https://blogs.oracle.com/dave/entry/java_util_concurrent_reentrantlock_vs). –

+0

@AlexanderRyzhov gracias por dejarme señalar al autor original de este artículo, mientras lo leía mientras me estaba preparando para mi scjp de otro blog ... muchas gracias –

4
  1. Debido a que su punto de referencia es defectuoso: en un caso de uso real, el tiempo necesario para producir y consumir los elementos de la cola es mucho más importante que el tiempo que lleva agregar y eliminar un elemento de la cola o desde ella. Por lo tanto, el rendimiento bruto de la cola no es tan importante. Por cierto, el código solo muestra cómo tomar elementos de la primera implementación de la cola y no cómo los agrega. Además, la elección de la estructura adecuada no se basa en el rendimiento, sino en el comportamiento. Si quieres algo concurrente, eliges una cola de bloqueo, porque está implementada para ti y no tiene errores como tu código. Si desea FIFO (que a menudo es lo que desea), no elegirá un TreeSet.

  2. Si desea comparar sincronización frente a ReentrantLock, no debe utilizar una estructura de datos para uno y otra estructura de datos para el otro. ReentrantLock solía ser más rápido, pero están en el mismo nivel, hoy en día (si creo lo que dice Brian Goetz en JCIP). De todos modos, elegiría uno sobre el otro por razones de seguridad/capacidad. No por motivos de rendimiento.

+0

No estoy de acuerdo con que el los criterios de decisión no deben considerar el desempeño. Un enfoque típico sería hacer que la solución funcione correctamente, luego considerar el rendimiento. Pero no hay absolutamente nada de malo en considerarlo de antemano, ya que puede ayudar a ahorrar más tiempo refactorizando más tarde. – Brady

+0

@JB Nizet: - ¿Qué error contiene mi código? Tengo el entendimiento similar de que para FIFO, no debería usar un TreeSet, pero dime el motivo si el método put de TreeSet es mucho más rápido que el método add de LinkedList (porque el tiempo de construcción del objeto es alto), por qué wouldn ¿Uso un TreeSet (aunque la eliminación es muy rápida en el caso de una LinkedList)? Para las aplicaciones comerciales en tiempo real, solo el rendimiento importa. Entonces, el quid de la historia es que quiero usar un mejor DS para Queue y quiero usar REL sobre sincronizado. – 100pipers

+1

@Brady Claro, no empieces con un diseño estúpido que obviamente tendrá un mal rendimiento, pero no deberías elegir una implementación específica basada en una corazonada de rendimiento. Elija el que mejor se adapte a las necesidades de su aplicación y tendrá menos errores. Si y solo si el rendimiento se convierte en un * problema *, busque formas de mejorarlo mediante el intercambio de implementaciones. – Bohemian