Patrón de diseño: una breve introducción al patrón singleton

Principio del modo singleton

¿Qué es un objeto singleton?

Para algunos objetos, solo necesitamos uno como grupo de subprocesos, fuente de datos de caché, dispositivo de hardware, etc. Si hay varias instancias, causará conflictos y resultados inconsistentes. Después de todo, tú tienes y yo tengo, pero lo que tienes y lo que tengo puede no ser exactamente lo mismo, son lo mismo. El uso del patrón singleton puede garantizar que una clase tenga como máximo una instancia y proporcionar un punto de acceso global.

public class Test {
    public class ABC {
        public ABC() {
        }

//        private ABC() {  //为这个内部类申明私有的构造方法,外部则无法通过new初始化,只能类自己初始化
//        }
//        ABC n1 = new ABC();
    }

    public class CDB {
        public CDB() {
            ABC n1, n2;
            n1 = new ABC();
            n2 = new ABC();
            System.out.println("CBD: " + (n1 == n2));    //false
        }
    }

    public static void main(String[] args) {
        ABC n1, n2;
        n1 = new Test().new ABC();
        n2 = new Test().new ABC();
        System.out.println("main: " + (n1 == n2));   //false
        new Test().new CDB();
    }
}

Entonces, ¿hay alguna forma de hacer que el nuevo objeto sea el mismo cada vez? Mira el diagrama de clases de patrón singleton a continuación, ¡puedes encontrar algunas ideas!

Singleton (singleton)
static uniqueInstance (declaración de objeto único estático)
singleton privado () (método de instanciación privada)
static getInstance () (punto de acceso global)

Codificación de combate

Después de comprender el contenido anterior, escribamos un código simple en modo singleton, el código es el siguiente:

public class Singleton {
    private static Singleton uniqeInstance = null;    //静态变量

    private Singleton() {    // 私有的构造方法,外部无法使用
    }

    public static Singleton getInstance() {
        if (uniqeInstance == null) {
            uniqeInstance = new Singleton();
        }
        return uniqeInstance;
    }
}

Dado que las variables estáticas no pertenecen a ningún objeto de instancia, pertenecen a clases, por lo que solo habrá una copia en la memoria.Durante el proceso de carga de la clase, JVM asigna espacio de memoria para las variables estáticas una vez.

Imaginemos este escenario: una fábrica de alimentos, solo hay una fábrica y luego solo hay una olla en la fábrica. Después de hacer un lote de alimentos, podemos hacer el siguiente lote. En este momento, nuestro objeto de fábrica de alimentos es un singleton. La siguiente es la implementación de la simulación. El código, la implementación singleton del código es diferente de la implementación simple anterior, se ha optimizado y explicaré por qué está optimizado más adelante.

public class ChocolateFactory {
    private boolean empty;   // 空锅
    private boolean boiled;  // 加热
    public volatile static ChocolateFactory uniqueInstance = null;

    private ChocolateFactory() {
        empty = true;    // 锅是空的
        boiled = false;  // 还没加热
    }

    public static ChocolateFactory getInstance() {
        if (uniqueInstance == null) {
            synchronized (ChocolateFactory.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new ChocolateFactory();
                }
            }
        }
        return uniqueInstance;
    }
    // 第一步装填
    public void fill() {
        if (empty) {  // 锅是空的
            // 添加原料巧克力动作
            empty = false;  // 锅装满了,不是空的
            boiled = false;  // 还没加热
        }
    }
    // 第三步倒出
    public void drain() {
        if ((!empty) && boiled) {  // 锅不是空的,已经加热
            // 排出巧克力动作
            empty = true;   //出锅,锅空了
        }
    }
    // 第二步加热
    public void boil() {
        if ((!empty) && (!boiled)) {  // 锅不是空的,没加热
            // 煮沸
            boiled = true;  // 已经加热
        }
    }
}

Problemas y optimización del modo singleton

problema

En el caso del subproceso múltiple, existirá el concepto de segmentos de tiempo y competencia de CPU. ¿Esto es exactamente lo que sucede cuando pueden ocurrir problemas en el modo singleton? Tomemos como ejemplo el código de la fábrica de procesamiento de alimentos

public synchronized static ChocolateFactory getInstance() {
       if (uniqueInstance == null) {
           uniqueInstance = new ChocolateFactory();
       }
       return uniqueInstance;
   }

En el caso de multiproceso, se instanciarán dos objetos

Solución optimizada

Método getInstance sincronizado (sincronizado)

El subproceso 1 se ejecuta if (uniqueInstance == null), y el subproceso 2 roba el derecho de ejecución. En este momento, el subproceso 1 no tiene ningún objeto nuevo; el subproceso 2 también llegó if (uniqueInstance == null)y descubrió que no había una instancia de objeto y planeó crear una instancia del objeto; finalmente, el subproceso 1 y el subproceso 2 se ejecutarán uniqueInstance = new ChocolateFactory();en este momento. El modificador sincronizado se agrega antes del método getInstance () . Sin embargo, este método consume más rendimiento cuando las llamadas multiproceso son frecuentes.

Cree una instancia "con entusiasmo"

public class ChocolateFactory {
    public static ChocolateFactory uniqueInstance = new ChocolateFactory();  //“急切”创建实例
    public static ChocolateFactory getInstance() {
       if (uniqueInstance == null) {
           uniqueInstance = new ChocolateFactory();
       }
       return uniqueInstance;
   }
}

public static ChocolateFactory uniqueInstance = new ChocolateFactory();El objeto de instancia se carga e inicializa una vez cuando se inicia la aplicación. En este momento, las llamadas de subprocesos múltiples siempre tendrán solo una instancia porque if (uniqueInstance == null)el resultado siempre es falso; pero si el par de objetos singleton no se usa en la aplicación, use El método consume algo de espacio en la memoria

Verifique y bloquee (mejor)

public class ChocolateFactory {
    //用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。
    public volatile static ChocolateFactory uniqueInstance = null;   
    public static ChocolateFactory getInstance() {
        if (uniqueInstance == null) {
            synchronized (ChocolateFactory.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new ChocolateFactory();
                }
            }
        }
        return uniqueInstance;
    }
}

En primer public volatile static ChocolateFactory uniqueInstance = null;lugar, el objeto no se inicializa cuando se inicia la aplicación, lo que ahorra memoria; en segundo lugar , el bloque de código modificado por sincronizado se vuelve a if (uniqueInstance == null) {}juzgar, y solo cuando se cumplen las condiciones ingresará al método de sincronización, reduciendo el consumo de rendimiento.

Supongo que te gusta

Origin blog.csdn.net/doubututou/article/details/109210319
Recomendado
Clasificación