java - patterns - Bloqueo controlado doble en Singleton
journaldev java design patterns (4)
aquí está mi clase personalizada para el patrón Singleton. en este código, utilizo el bloqueo con doble verificación como se muestra a continuación. A medida que leo muchas publicaciones sobre alguna fuente, dicen que la verificación doble es útil porque evita que dos hilos simultáneos se ejecuten al mismo tiempo y que formen dos objetos diferentes.
public class DoubleCheckLocking {
public static class SearchBox {
private static volatile SearchBox searchBox;
// private constructor
private SearchBox() {}
// static method to get instance
public static SearchBox getInstance() {
if (searchBox == null) { // first time lock
synchronized (SearchBox.class) {
if (searchBox == null) { // second time lock
searchBox = new SearchBox();
}
}
}
return searchBox;
}
}
Todavía no entiendo mucho el código anterior. ¿Cuál es el problema, si dos hilos juntos ejecutan la misma línea de código cuando la instancia es nula?
if (searchBox == null) {
synchronized (SearchBox.class) {
if (searchBox == null) {
searchBox = new SearchBox();
}
}
}
Cuando eso aparece. ambos hilos verán que el objeto es nulo. entonces ambos se sincronizan. y luego, lo vuelven a comprobar, y todavía lo ven nulo . y crea dos objetos diferentes OOOPS.
Por favor explique por mi ¿Qué he entendido mal?
Gracias :)
Este bloqueo de doble verificación solo es necesario si le preocupan muchos hilos que llaman al singleton simultáneamente, o el costo de obtener un candado en general.
Su propósito es evitar la sincronización innecesaria, lo que mantiene su código rápido en un entorno de múltiples subprocesos.
Consulte este enlace para obtener más información.
Si está ejecutando Java 1.5 o una versión superior y usa la palabra clave volatile
en su mecanismo de verificación doble bloqueada, funcionará correctamente. A medida que utiliza la palabra clave volatile
, su ejemplo no se rompe según el mismo enlace anterior.
No, ya que está obteniendo el bloqueo en SearchBox.class
, solo un hilo ingresará al bloque sincronizado a la vez. Entonces, el primer hilo entra y luego busca searchBox
y lo crea y luego sale del bloque sincronizado, luego el segundo hilo ingresa al bloque y luego descubre que searchBox
no es nulo porque el primer hilo ya lo creó para que no cree una nueva instancia de searchBox
El patrón de verificación doble se utiliza para evitar la obtención del bloqueo cada vez que se ejecuta el código; si la llamada no ocurre conjuntamente, la primera condición fallará y la ejecución del código no ejecutará el bloqueo, lo que ahorrará recursos.
Veamos este código:
1 if (searchBox == null) {
2 synchronized (SearchBox.class) {
3 if (searchBox == null) {
4 searchBox = new SearchBox();
5 }
6 }
Tratemos de razonar sobre esto. Digamos que tenemos dos hilos A
y B
y supongamos que al menos uno de ellos llega a la línea 3 y observa que searchBox == null
es true
. Dos hilos no pueden estar en la línea 3 al mismo tiempo debido al bloque synchronized
. Esta es la clave para entender por qué funciona el bloqueo comprobado. Entonces, debe darse el caso de que A
o B
synchronized
primero synchronized
. Sin pérdida de generalidad, di que ese hilo es A
Luego, al ver que searchBox == null
es verdadero, ingresará el cuerpo de la declaración y establecerá searchBox
en una nueva instancia de SearchBox
. Eventualmente saldrá del bloque synchronized
. Ahora será el turno de B
de ingresar: recuerde, B
fue bloqueado esperando que A
salga. Ahora cuando ingrese al bloque, observará searchBox
. Pero A
habrá dejado simplemente establecer searchBox
en un valor no null
. Hecho.
Por cierto, en Java, la mejor forma de implementar un singleton es usar un tipo de elemento enum
. De Java efectivo :
Si bien este enfoque aún no se ha adoptado ampliamente, un tipo de enumeración de elemento único es la mejor manera de implementar un singleton.
if (searchBox == null) { //1
synchronized (SearchBox.class) {
if (searchBox == null) { //2
searchBox = new SearchBox();
}
}
}
}
- Si ya se creó una instancia, no haga nada, evite bloquear los hilos
- El primer hilo que ha adquirido el bloqueo comprueba y ve que no existe tal objeto y lo crea. Libera el bloqueo y el segundo puede hacer lo mismo: tiene que verificar si el objeto existe porque el primero puede haberlo creado.
Así que, básicamente, el if
externo se usa para evitar bloqueos redundantes: permite que todos los hilos sepan que ya hay un objeto y que no necesitan bloquear / hacer nada. Y el if
interno se usa para permitir que un hilo concurrente sepa si otro ya ha creado el objeto o no.