zoukankan      html  css  js  c++  java
  • 单例模式的理解和示例

    一、是什么

    确保一个类只有一个实例,并提供一个全局访问点

    一般分类两大类: 饿汉模式、懒汉模式

    使用: 以前在线白鹭H5游戏时,因为有很多的场景类, 而每个场景类不需要创建很多遍, 所以使用单例模式

    二、示例

    1. 饿汉模式

    /**
     * 饿汉模式, 线程安全, 但默认就创建实例, 占用空间
     */
    public class Singleton1 {
        private static final Singleton1 instance = new Singleton1();
    
        private Singleton1() {
        }
    
        public static Singleton1 getIntance() {
            return instance;
        }
    }

    用空间换时间,默认就创建实例,所以没有线程安全问题

    2. 懒汉模式

    /**
     * 懒汉模式, 线程不安全
     */
    public class Singleton2 {
        private static Singleton2 instance = null;
    
        private Singleton2() {
    
        }
    
        public static Singleton2 getInstance() {
            if (instance == null) {
                instance = new Singleton2();
            }
    
            return instance;
        }
    }

    现在不安全在于,多个线程访问getInstance()时,当一个线程已经初始化了,而另外一个线程并没有感知,又重新创建了实例,这时候就不是单例

    2.1 双检锁 Double-check

    /**
     * 懒汉模式--双检索
     */
    public class SingletonDoubleCheck {
        private static SingletonDoubleCheck instance = null;
    
        private SingletonDoubleCheck() {
    
        }
    
        public static SingletonDoubleCheck getInstance() {
            if (instance == null) {
                synchronized (SingletonDoubleCheck.class) {
                    if (instance == null) {
                        instance = new SingletonDoubleCheck();
                    }
                }
            }
    
            return instance;
        }
    }

    为了在懒汉模式的基础上,保证线程安全, 出现了双检锁的设计,但是有出现了另一个问题。

    在new SingletonDoubleCheck()时,是非原子性的,实际分为三步

    1. new 分配内存空间
    2. 初始化对象
    3. 将对象指向刚分配的内存空间

    但JVM编译器,为了性能考虑,可能重新排序2,3两个, 变为:

    1. new 分配内存空间
    2. 将对象指向刚分配的内存空间
    3. 初始化对象

    举例说明

      线程1检查到instance为空,获取锁,再次检查instance为空,为instance分配内存空间,指向内存空间,这时线程2检查到instance不为空,直接返回instance,但此时对象还没有初始化完成

    2.2 双检锁 线程安全

    /**
     * 使用volatile关键字的双检锁
     */
    public class SingletonDoubleCheck2 {
        /**
         * volatile关键字保证我在锁instance时, 禁止JVM重排序
         */
        private volatile static SingletonDoubleCheck2 instance = null;
    
        private SingletonDoubleCheck2() {
    
        }
    
        public static SingletonDoubleCheck2 getInstance() {
            if (instance == null) {
                // 再次减少锁的范围, 只锁instance变量
                synchronized (instance) {
                    if (instance == null) {
                        instance = new SingletonDoubleCheck2();
                    }
                }
            }
    
            return instance;
        }
    }

    使用volatile关键字来禁止JVM重排序

    3.3 内部类实现

    /**
     * 静态内部类实现 -- 延迟加载
     *
     * 天生线程安全
     */
    public class Singleton3 {
    
        /**
         * 私有化构造
         */
        private Singleton3() {
            System.out.println("初始化");
        }
    
        /**
         * 静态内部类
         */
        private static class InnerObject{
            private static Singleton3 instance = new Singleton3();
        }
    
        public static Singleton3 getInstance() {
            return InnerObject.instance;
        }
    
        public static void main(String[] args) {
            getInstance();
        }
    }

    3.4 静态代码实现

    /**
     * 懒汉模式 -- 静态代码块实现
     */
    public class Singleton4 {
    
        private static Singleton4 instance = null;
    
        static {
            instance = new Singleton4();
        }
    
        public static Singleton4 getInstance() {
            return instance;
        }
    }

    外部类加载时并不需要立即加载内部类,所以可以起到延时加载的目录,

    三、总结

    单例模式是一个创建型的设计模式,能够帮助开发者创建一个唯一的实例

    使用的还是挺频繁的

  • 相关阅读:
    【C++】深度探索C++对象模型读书笔记--关于对象(Object Lessons)
    【操作系统】调度算法
    【vim】vim常用命令
    【linux】linux的数据流重定向
    以太网帧,IP,TCP,UDP首部结构
    IPv4编址及子网划分
    【计算机网络】计算机网络模型
    【计算机网络】NAT:网络地址转换
    【设计模式】C++中的单例模式
    (转)linux查找技巧: find grep xargs
  • 原文地址:https://www.cnblogs.com/milicool/p/11278613.html
Copyright © 2011-2022 走看看