• 【设计模式】单例模式


    一、概念

    单例模式是设计模式中最为简单和为人熟知的一种设计模式,属于创建型模式的一种。

    定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    UML图表示法:

    Singleton
    -uniquenstance:Singleton

    -Singleton():void

    +getInstance():Singletion

    +operation():void

      图1 单例模式结构图

    单例模式具有以下几个特点:
    ①单例类只能有一个实例
    ②单例类必须自己创建自己的唯一实例
    ③单例类必须给所有其他对象提供这一实例

    二、示例代码

    1、懒汉式:

    package com.qunar.base.collection;
    
    public class Singleton {
        /** 定义一个私有的静态变量来存储创建的实例 **/
        private static Singleton uniqueInstance = null;
    
        /** 私有化构造方法,防止外部类来创建此类的实例 **/
        private Singleton() {
        }
    
        /**
         * 定义一个public的静态方法,向外界提供仅有的这个实例
         * 
         * @return
         */
        public static synchronized Singleton getInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    
        /**
         * 实例方法,外界只能通过仅有的实例来调用此方法
         */
        public void operate() {
    
        }
    
    }

    2、饿汉式

    package com.qunar.base.collection;
    
    public class Singleton {
        /** 定义一个私有的静态变量来存储创建的实例,直接在声明变量时创建此实例 **/
        private static Singleton uniqueInstance = new Singleton();
    
        /** 私有化构造方法,防止外部类来创建此类的实例 **/
        private Singleton() {
        }
    
        /**
         * 定义一个public的静态方法,向外界提供仅有的这个实例
         * 
         * @return
         */
        public static Singleton getInstance() {
            // 直接返回已经创建好的实例
            return uniqueInstance;
        }
    
        /**
         * 实例方法,外界只能通过仅有的实例来调用此方法
         */
        public void operate() {
    
        }
    
    }

    三、模式讲解

    1、单例模式的优缺点

      a、时间和空间

      懒汉式是典型的时间换空间,而饿汉式是典型的空间换时间。

      b、线程安全

      不加同步的懒汉式是线程不安全的

      饿汉式是线程安全的

    2、双重检查加锁方式实现的单例模式

      代码如下:

    package com.qunar.base.collection;
    
    public class Singleton {
        /** 对保存实例的变量添加volatile修饰 **/
        private volatile static Singleton uniqueInstance = null;
    
        /** 私有化构造方法,防止外部类来创建此类的实例 **/
        private Singleton() {
        }
    
        /**
         * 定义一个public的静态方法,向外界提供仅有的这个实例
         * 
         * @return
         */
        public static Singleton getInstance() {
            // 先检查实例是否存在,如果不存在,才会进入下面的同步块
            if (uniqueInstance == null) {
                // 同步快,线程安全的创建实例
                synchronized (Singleton.class) {
                    // 再次检查是否存在,如果不存在才真正的创建实例
                    if (uniqueInstance == null) {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    
        /**
         * 实例方法,外界只能通过仅有的实例来调用此方法
         */
        public void operate() {
    
        }
    
    }

    在上面的代码中,如果没有对变量uniqueInstance添加volatile修饰,则可能会产生以下问题:

    out-of-order write问题(详见 DoubleCheckedLocking):

    instance = new Singleton();  的顺序应该是

    1 分配内存
    2 构造函数初始化
    3 将对象的reference赋值给instance

    但因为Java Memory Model的问题,可能出现下面的所谓out-of-order write的问题:

    1 分配内存
    2 将对象的reference赋值给instance
    3 构造函数初始化

    也就是还没对对象初始化,就已经instance != null了,这样如果另外一个线程这时候对实例进行操作,可能有意想不到的结果。

    四、单例模式的调用顺序示意图
            由于单例模式有两种实现方式,那么它的调用顺序也分成两种。先看懒汉式的调用顺序,如图2所示:


                            图2  懒汉式调用顺序示意图

    饿汉式的调用顺序,如图3所示:


                                   图3  饿汉式调用顺序示意图

    五、应用场景

         在我们的应用中,有一个类AppConfig,用以读取配置文件中的配置信息,并在程序中需要配置信息时,被使用,由于配置信息是全局性的,因此在整个应用中,仅需要一个该类的实例,这样只需要读取一次配置文件,并且也会节省内存,避免不必要的内存浪费,此时就可以采用单例模式。

  • 相关阅读:
    洛谷 P2634 BZOJ 2152 【模板】点分治(聪聪可可)
    洛谷 P3819 松江1843路
    洛谷 P1005 矩阵取数游戏
    洛谷 P2712 摄像头
    洛谷 P2774 方格取数问题
    洛谷 P3369 BZOJ 3224 【模板】普通平衡树(Treap/SBT)
    洛谷 P2805 BZOJ 1565 植物大战僵尸
    洛谷 P2312 解方程
    洛谷 P3355 骑士共存问题
    洛谷 P2762 太空飞行计划问题
  • 原文地址:https://www.cnblogs.com/yangfengtao/p/2731527.html
Copyright © 2020-2023  润新知