zoukankan      html  css  js  c++  java
  • 设计模式之单例补遗

    懒汉(双重检查锁定)方式的详解补充

    书接上回,还是先把代码再贴一次。

    public class LazySingleton {
        private static volatile LazySingleton instance;
        private LazySingleton() {}
        public static LazySingleton getInstance() {
            if (instance == null) {
                synchronized(LazySingleton.class){
                    if (instance == null) {
                        instance = new LazySingleton();
                    }
                }
            }
            return instance;
        }
    }
    

    关键之处还是在声明instance变量时的volatile关键字。因为咋看起来,如果没有这个volatile似乎程序逻辑上也没有什么漏洞。但是,由于instance = new LazySingleton()的操作并不是原子的,而是按以下步骤执行:

    1. 为新对象分配内存空间;
    2. 构造并初始化对象;
    3. 把instance指向步骤1中分配的内存空间。

    除此之外,Java运行时环境的JIT编译器会做指令重排序操作,即生成的机器指令与字节码指令顺序不一致。在单线程条件下,即使是步骤2、3发生了重排序,可以保证最终结果保证一致,也就是instance指向来初始化后的对象。而在并发场景下,一旦发生了重排序操作,就有可能出现instance指向了步骤1分配的内存,单尚未完成步骤2的初始化。

    此时就轮到『volatile』闪亮登场了。Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

    如果希望对java的重排序有更多了解,可以先读一下这篇文章

    枚举实现

    虽然已经提供了很多种的单例实现方法,但是(在Java中)最简洁的莫过于使用枚举来实现了。

    public enum EnumSingleton {
        INSTANCE;
        
        public void Method() {}
    }
    

    不仅代码简洁,而且JVM来保证了其线程安全,并且无偿地提供了序列化机制,绝对防止多次实例化,是一种高效安全的单例模式的实现。

    但是这种写法我是很少使用的,纯属个人习惯问题,总觉得写法上有些怪异,使用起来有心里负担,而且其它语言中似乎也没见过有这样的用法。

    扩展单例(多例?)

    之前我们讨论的都是纯粹的单例,应用程序生命周期中只存在一个实例。而现在我们需要做一下扩展,将程序改造为允许在其声明周期内存在指定个数的实例,是一种类似于数据库连接池这样的概念。明确概念之后,代码实现也比较简单。

    public class Singleton {
    
        private static int size = 5;
        private static ArrayList<E> instanceList = new ArrayList(size);
    
        static {
            for(int i = 0; i < size; i++) {
                instanceList.add(new Singleton());
            }
        }
        private Singleton(){}
    
        public static Singleton getInstance(){
            Random random = new Random();
    
            return (Singleton) instanceList.get(random.nextInt(size));
        }
    }
    

    后记

    关于单例模式,暂告一段落,后面再继续聊聊其它的模式。

    TO BE CONTINUED

    知识共享许可协议
    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

    http://bigman.pw
  • 相关阅读:
    创建目录(单个目录和多级子目录)方法
    C++中创建目录
    C/C++中判断某一文件或目录是否存在
    Eclipse快捷键大全(转载)
    关于注册模型失败的分析
    框架Model注册失败
    nop中导航属性的写法
    CodeFirst中导航属性的代码实现 理解
    MVC下验证码
    Androidi学习笔记 1
  • 原文地址:https://www.cnblogs.com/renzhiwei/p/7767234.html
Copyright © 2011-2022 走看看