Pregunta

Checkstyle informa este código como "El idioma de bloqueo de doble verificación está roto", pero no creo que mi código en realidad se vea afectado por los problemas con el bloqueo de doble verificación.

Se supone que el código crea una fila en una base de datos si no existe una fila con esa identificación. Se ejecuta en un entorno de subprocesos múltiples y quiero evitar las excepciones SQL de clave principal.

El pseudocódigo:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

Estoy de acuerdo en que parece un bloqueo doblemente verificado, pero no estoy usando variables estáticas y el código en fetch () y create () es probablemente demasiado complejo para estar en línea y fuera de orden.

¿Estoy equivocado o checkstyle? :)

¿Fue útil?

Solución

Suponiendo que desea leer la línea más interna:

row = dao().create(id);

No es un problema clásico de bloqueo de doble verificación asumiendo que dao (). fetch está correctamente muteado del método de creación.

Editar : (se actualizó el código)

El problema clásico de un bloqueo doblemente verificado es tener un valor asignado antes de que se produzca la inicialización donde dos hilos acceden al mismo valor.

Suponiendo que el DAO está sincronizado correctamente y no devolverá un valor parcialmente inicializado, esto no sufre los defectos del idioma de bloqueo doblemente verificado.

Otros consejos

Creo que en este caso, checkstyle es correcto. En el código presentado, considere lo que sucedería si dos hilos tuvieran row == null en la entrada del bloque sincronizado. El hilo A entraría en el bloque e insertaría la nueva fila. Luego, después de que el subproceso A sale del bloque, el subproceso B ingresará al bloque (porque no sabe lo que acaba de suceder) e intentará insertar la misma nueva fila nuevamente.

Veo que acaba de cambiar el código y agregó una línea faltante bastante importante allí. En el código nuevo , es posible que pueda salirse con la suya, ya que dos hilos no dependerán de los cambios en una variable compartida (estática). Pero es mejor que vea si su DBMS admite una declaración como INSERT OR UPDATE .

Otra buena razón para delegar esta funcionalidad al DBMS es si alguna vez necesita implementar más de un servidor de aplicaciones. Dado que los bloques sincronizados no funcionan en todas las máquinas, de todos modos tendrá que hacer algo más en ese caso.

Si está tentado a escribir código como este, considere:

  • Desde Java 1.4, los métodos de sincronización se han vuelto bastante baratos. No es gratis, pero el tiempo de ejecución realmente no sufre tanto que vale la pena correr el riesgo de corrupción de datos.

  • Desde Java 1.5, tiene las clases Atomic * que le permiten leer y establecer campos de forma atómica. Lamentablemente, no resuelven su problema. Por qué no agregaron AtomicCachedReference o algo así (que llamaría a un método reemplazable cuando se llama a get () y el valor actual == nulo) está más allá de mí.

  • Pruebe ehcache . Le permite configurar un caché (es decir, un objeto que le permite llamar al código si una clave no está contenida en un mapa). Esto suele ser lo que desea y los cachés realmente resuelven su problema (y todos esos otros problemas que ni siquiera sabía que existían).

Como otros han señalado, este código hará lo que pretendes como es, pero solo bajo un conjunto estricto de suposiciones no obvias:

  1. El código Java no está agrupado (consulte la respuesta de @Greg H)
  2. La " fila " la referencia es solo que se verifica para nulo en la primera línea, antes del bloque de sincronización.

La razón por la cual el idioma de bloqueo de doble verificación está roto (según la sección 16.2.4 de Concurrencia de Java en la práctica ) es que es posible que un subproceso que ejecuta este método vea una referencia no nula pero iniciada incorrectamente a " fila " ;, antes de ingresar el bloque sincronizado (a menos que " dao " proporcione una sincronización adecuada) . Si su método estuviera haciendo algo con " row " aparte de comprobar que es nulo o no, estaría roto. Tal como está, probablemente esté bien, pero muy frágil : personalmente, no me sentiría cómodo cometiendo este código si pensara que incluso existe una remota posibilidad de que algún otro desarrollador en algún momento posterior pueda modificar el método sin entender las sutilezas de DCL.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top