El papel de Volátil en DCL (verificación de doble bloqueo), cómo evitar que la reflexión genere instancias de objetos singleton

¿Por qué hay un modo DCL singleton? Creo que muchos de los modos singleton escritos por usted son el modo perezoso convencional sin bloqueo. De hecho, este tipo de escritura no tiene ningún efecto en un solo hilo. Una vez que se utiliza en varios hilos, se crearán varios objetos. Este no es un modo singleton. Para ser un modo singleton en multihilo, introdujimos el modo DCL singleton.

Antes de introducir el modo DCL singleton, primero comprendamos la palabra clave volátil:

La variable modificada por el modificador Volatile asegura que esta instrucción no se omitirá debido a la optimización del compilador cuando el compilador compila.

Principales características:

  • Asegurar visibilidad en tiempo real cuando diferentes hilos modifican la variable al mismo tiempo, es decir, cuando un hilo modifica la variable, otros hilos son visibles en tiempo real;
  • Está prohibido reordenar instrucciones (especialmente importante);

Flujo de instrucciones DCL singleton:

  1. Asignar memoria para el objeto;
  2. Inicialice el objeto de instancia;
  3. Asignar memoria para referencias a objetos;

Para optimizar las instrucciones y mejorar la eficiencia de la operación del programa, la JVM permite el reordenamiento de instrucciones. Si la JVM optimiza las instrucciones para ejecutar 1, 3 y 2 secuencialmente, cuando varios subprocesos ejecutan tareas al mismo tiempo, si la instrucción 3 se acaba de ejecutar, la instrucción 2 se ejecutará en el futuro y otras Cuando el hilo llama a getInstance () para ejecutar la tarea, lanzará una excepción de objeto no inicializado ; por lo tanto, para resolver este problema, citamos la palabra clave Volatile para modificar la variable.

public class SingleDemo {
    private static volatile SingleDemo instance;

    private SingleDemo() {
    }

    public static SingleDemo getInstance() {
        if (instance == null) {//不用volatile修饰的话,线程二提前访问指令顺序被打乱,认为不为空跳过判断
            synchronized (SingleDemo.class) {
                if (instance == null) {
                    instance = new SingleDemo();//不用volatile修饰的话,线程一指令顺序被打乱未来及创建对象
                }
            }
        }
        return instance;//不用volatile修饰的话,线程二直接拿到instance
    }
}

Después de leer el código anterior, se estima que algunos estudiantes preguntarán por qué el bloqueo de sincronización debe escribirse en el método en lugar de directamente en el nombre del método externo, así:

    public static synchronized SingleDemo getInstance() {
        if (instance == null) {
            instance = new SingleDemo();
        }
        return instance;
    }

Aunque esto garantiza la seguridad de múltiples subprocesos, causará una gran sobrecarga de rendimiento. El bloqueo solo debe usarse durante la primera inicialización, y las llamadas posteriores no necesitan bloquearse, por lo que para resolver este problema DCL ( Comprobación de doble bloqueo) nació el modo singleton. Por supuesto, el bloqueo de sincronización también puede evitar que la reflexión destruya el objeto singleton instanciado;

Algunos estudiantes pueden preguntar por qué no es necesario sincronizar (esto) para un bloqueo sincronizado, como este:

    public static SingleDemo getInstance() {
        if (instance == null) {//不用volatile修饰的话,线程二提前访问指令顺序被打乱,认为不为空跳过判断
            synchronized (this) {
                if (instance == null) {
                    instance = new SingleDemo();//不用volatile修饰的话,线程一指令顺序被打乱未来及创建对象
                }
            }
        }
        return instance;//不用volatile修饰的话,线程二直接拿到instance
    }

Esto implica el tipo de bloqueo: bloqueo de método, bloqueo de objeto, bloqueo de clase:

  1. El bloqueo de método es, por ejemplo, el público SingleDemo getInstance () sincronizado estático del método de modificación sincronizado. La función es que el método solo se puede pasar cuando varios subprocesos llaman al método al mismo tiempo, y el alcance es cuando todos los objetos llaman al método;
  2. El bloqueo de objeto está, por ejemplo, sincronizado (esto). La función es que cuando varios subprocesos llaman al bloqueo de objeto al mismo tiempo, solo se puede pasar uno. Si varios objetos son responsables de la falla, el valor del alcance está en este objeto;
  3. El bloqueo de clase está, por ejemplo, sincronizado (SingleDemo.class). La función es que solo un objeto puede ser creado por varios subprocesos al mismo tiempo. El alcance es que cuando la clase solo existe para crear el objeto, el objeto se invalida cuando se llama;

Por lo tanto, el método anterior de usar bloqueos de objetos es incorrecto. Aquí se crean objetos por instanciación y se deben usar bloqueos de clases.

Supongo que te gusta

Origin blog.csdn.net/xhf_123/article/details/108468393
Recomendado
Clasificación