Pregunta

Considere el modismo de verificación doble para la inicialización diferida de los campos de instancia:

// Item 71 in Effective Java copied from this interview with Bloch.
private volatile FieldType field;
FieldType getField() {
    FieldType result = field;
    if (result == null) { // First check (no locking)
        synchronized(this) {
            result = field;
            if (result == null) // Second check (with locking)
                field = result = computeFieldValue();
        }
    }
     return result;
}

Quiero poder restablecer el campo de forma segura (forzarlo a cargar nuevamente desde la base de datos, en mi caso). Supongo que podríamos hacer esto teniendo un método de reinicio:

void reset() {
   field = null;
}

¿Es esta la forma estándar de restablecer el campo? ¿Es seguro? ¿Alguna trampa? Lo pregunto porque Bloch dio la siguiente advertencia sobre la carga diferida verificada dos veces: `` El idioma es muy rápido pero también complicado y delicado, así que no sienta la tentación de modificarlo de ninguna manera. Simplemente copie y pegue, normalmente no es una buena idea, pero aquí es apropiado. & Quot;

Gracias de antemano, Playa del Himalaya.

¿Fue útil?

Solución

Sí, esto es seguro para subprocesos.

El bloque sincronizado es para evitar que varios subprocesos llamen innecesariamente a computeFieldValue () . Como field es volátil, los accesos en reset y getField están todos bien ordenados.

Si la primera comprobación no es nula, se realiza getField ; result se devuelve.

De lo contrario, se adquiere un bloqueo, excluyendo cualquier otro subproceso que pueda establecer el campo como no nulo, pero permitiendo que cualquier subproceso establezca field en nulo. Si algún hilo establece field en nulo, nada debería haber cambiado; Esa es la condición que llevó el hilo al bloque sincronizado. Si otro subproceso ya había adquirido el bloqueo después de la verificación del subproceso actual y establece el campo en un valor no nulo, la segunda verificación lo detectará.

Otros consejos

Creo que esto debería ser seguro, pero solo porque está almacenando el campo en una variable local. Una vez hecho esto, no hay forma de que la referencia de la variable local cambie mágicamente a nulo, incluso si otro subproceso está restableciendo el valor del campo a mitad de camino.

Parece que esto funcionará siempre que el método de reinicio sea el método reset () mencionado anteriormente. Sin embargo, si el método reset () crea una instancia de un nuevo Objeto (como se muestra a continuación), ¿no podría terminar devolviendo algo diferente de lo que pretendía?

void reset() {
    field = new FieldType();
}

Supongo que depende exactamente de lo que quieres decir con hilo seguro.

Podría terminar con una situación en la que se usa una primera instancia después de una segunda. Eso puede estar bien, o puede que no.

Creo que el método reset () no es correcto. Si lee el Artículo 71, encontrará:

Este código puede parecer un poco complicado. En particular, la necesidad de lo local El resultado variable puede no estar claro. Lo que hace esta variable es garantizar que el campo sea solo lectura una vez en el caso común donde ya está inicializado.

La inicialización diferida no supone que el campo pueda cambiar. Si el campo se establecerá en nulo entre estos operadores:

FieldType result = field;
if (result == null) {// Primera comprobación (sin bloqueo)

getField () proporciona un resultado incorrecto.

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