zoukankan      html  css  js  c++  java
  • 设计模式---单例模式

    前言

    单例也是被嚼烂了的设计模式之一,但是这一模式在实际中确实使用非常广泛,今天,使用多个版本的单例模式实现,来讲一下实现单例需要注意的一些地方

    版本1

    使用静态字段,使用单例方法(属性)进行获取,第一次访问时进行初始化,以后直接返回

    public sealed class Singleton
    {
        // 私有构造函数,禁止外部访问
        private Singleton() { }
    
        // 静态实例字段
        private static Singleton _instance;
    
        // 静态实例方法
        public static Singleton Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new Singleton();
    
                return _instance;
            }
        }
    }

    版本2

    OK,单例方法的设计需要考虑多线程调用,所以线程同步是必须考虑的,否则可能就不是真的“单例”了

    public sealed class Singleton
    {    
        // 私有构造函数,禁止外部访问
        private Singleton() { }
    
        // 静态实例字段
        private static volatile Singleton _instance;
        private static readonly object syncRoot = new object();
    
        // 静态实例方法
        public static Singleton Instance
        {
            get
            {
                lock (syncRoot)
                {
                    if (_instance == null)
                        _instance = new Singleton();
    
                    return _instance;
                }
            }
        }
    }
    
    

    其实在实际使用中,这个版本已经OK了,但是线程同步还可以做一点小小的优化,于是

    版本3

    public sealed class Singleton
    {
        // 私有构造函数,禁止外部访问
        private Singleton() { }
    
        // 静态实例字段
        private static volatile Singleton _instance;
        private static readonly object syncRoot = new object();
    
        // 静态实例方法
        public static Singleton Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (syncRoot)
                    {
                        if (_instance == null)
                            _instance = new Singleton();
                    }
                }
                return _instance;
            }
        }
    }

    OK,现在这个版本已经完美了,可以使用了,而且线程同步进行了优化,不用每一次调用都进行lock操作,但是等等,很多人还有另一种风格,就是在静态字段中提供内联初始化或者默认初始化,然后让属性或单例方法直接返回该静态字段。因为使用静态字段的初始化语法,其实可以保证线程安全(CLR是这么处理的),所以不用我们自己去编写线程同步代码

    版本4

    public sealed class Singleton
    {
        // 私有构造函数,禁止外部访问
        private Singleton()
        {
            Console.WriteLine("Singleton Constructed");
        }
    
        // 静态实例字段
        private static readonly Singleton _instance = new Singleton();
    
        // 静态实例方法
        public static Singleton Instance
        {
            get
            {
                return _instance;
            }
        }
    }
    
    

    OK,现在不用在获取实例的方法(属性)中写构造表达式了,更不用自己写线程同步的代码了,但是现在有一个问题,那就是字段的内联初始化的初始化时间是提前的,而且是不确定的,因为没有提供默认静态构造函数的话,静态字段的内联初始化会生成beforeInit标记,字段的初始化会在使用类型以前随机寻找一个时间来调用,这可以适度优化

    版本5

    public sealed class Singleton
    {
        // 私有构造函数,禁止外部访问
        private Singleton()
        {
            Console.WriteLine("Singleton Constructed");
        }
    
        // 默认静态构造函数
        static Singleton() { }
    
        // 静态实例字段
        private static readonly Singleton _instance = new Singleton();
    
        // 静态实例方法
        public static Singleton Instance
        {
            get
            {
                return _instance;
            }
        }
    }

    看到区别了么?现在只是加了一个静态构造函数,为了防止内联过早的进行调用,现在不会生出fieldBeforeInit信息,所以会到调用之前才进行初始化。但是关于调用的时机,是不是依然可以优化,优化到真正调用时才进行初始化呢?好了,请看下一个版本

    版本6

    public class Singleton
    {
        // 私有构造函数,禁止外部访问
        private Singleton()
        {
            Console.WriteLine("Singleton Constructed");
        }
    
        public static Singleton Instance
        {
            get { return NestedObject.NestedInstance; }
        }
    
        private class NestedObject
        {
            static NestedObject() { }
    
            public static readonly Singleton NestedInstance = new Singleton();
        }
    }

    这个版本使用内嵌的私有类作为一个容器来维持单例对象,使用静态初始化的方式并且支持延迟初始化,非常聪明。那么是否到此为止呢?其实对于静态初始化的延迟执行,.NET提供了非常方便的Lazy类,我们何不拿来一用,这样我们就不用自己编写内部类了

    版本7

    public class Singleton
    {
        private static Lazy<Singleton> _instance
            = new Lazy<Singleton>(() => new Singleton());
    
        // 私有构造函数,禁止外部访问
        private Singleton()
        {
            Console.WriteLine("Singleton Constructed");
        }
    
        public static Singleton Instance
        {
            get { return _instance.Value; }
        }
    }

    总结

    大家对单例模式的说法一向都是简约不简单,所以此模式虽然已被嚼烂,但它依然是作为程序员的必修课之一,这里面考察的主要是

    • 类的修饰符,一般建议使用 sealed,密封类,不允许继承
    • 类的构造函数,一般建议private,不允许外部访问
    • 类的执行时机,根据实际需求判断,是否饿汉式初始化或者惰性初始化
    • 类的初始化线程安全的考虑,必须考虑多线程访问时,构造仅执行一次

    实践中,可用的方式包括,版本3(单例方法初始化,加双检查锁定,延迟初始化)、版本4、5(静态初始化,非延迟初始化)、版本6、7(静态初始化,延迟实例化),其实都是可以使用的,具体选择哪个没有标准的,根据实际应用场景来选择吧

  • 相关阅读:
    Linux下wget下载软件小技巧以及安装jdk、tomcat与ftp服务器
    C++计算二叉树的节点数和高度
    转:POST 400 Bad Request The request sent by the client was syntactically incorrect
    SSM项目spring配置文件详细步骤(分门别类、灵巧记忆)
    IntelliJ IDEA里找不到javax.servlet的jar包
    51nod 1533 && CF538F
    51nod 1189
    51nod 1225
    51nod 1040
    51nod 1610
  • 原文地址:https://www.cnblogs.com/fecktty2013/p/designpatterns-singleton.html
Copyright © 2011-2022 走看看