zoukankan      html  css  js  c++  java
  • 不要用错单例模式

        SingleTon模式也许是被最广泛应用的模式,但是,最近看到的几个SingleTon不得不让我出一身冷汗。
        先来看看标准的反例:
    C#版
        public static SomeObject GetInstance()
        {
            
    if (_instance == null)
            {
                
    lock (_syncRoot)
                {
                    _instance 
    = new SomeObject();
                }
            }
            
    return _instance;
        }
    VB.Net版
        Public Shared Function GetInstance() As SomeObject
            
    If _instance Is Nothing Then
                
    SyncLock syncRoot
                    _instance 
    = New SomeObject()
                
    End SyncLock
            
    End If
            
    Return _instance
        
    End Function
        看到这段代码,相信大家都知道这个SingleTon的失败之处了吧,根本就是一个没有线程安全的SingleTon,用锁还用错地方了,汗水。。。假设创建SomeObject的时间比较长的话(例如访问数据库),有10个线程进来的话,创建出10个实例也是很有可能的,可以说是最失败的SingleTon。
        另外一种常见的错误是用双检锁的方式,这种错误的方式通常是因为JIT编译器优化而导致的,来看一下反例:
        public class BadSingleTon
        {
            
    private static readonly object _syncRoot = new object();
            
    private static BadSingleTon _instance;

            
    private BadSingleTon()
            {
                
    // label 5
            }

            
    public static BadSingleTon Instance
            {
                
    get
                {
                    
    // label 1
                    if (_instance == null)
                    {
                        
    // label 2
                        lock (_syncRoot)
                        {
                            
    // label 3
                            if (_instance == null)
                            {
                                
    // label 4
                                _instance = new BadSingleTon();
                            }
                        }
                    }
                    
    return _instance;
                }
            }
        }
        乍看上去,似乎很完美,当实例存在时,直接返回实例,不存在时去加锁,然后再判断是否存在实例(为什么?如果没有这个判断,就会和上面的例子一样,创建出多个实例),如果还是没有,那就创建实例。
        但是因为有编译器的优化,实际效果却有点不一样,假设现在有两个线程,分别是线程A和线程B,线程A先进入Instance属性,这时实例为空,线程A依次进入Label 1、2、3、4,然后创建对象,即Label 5,这时,如果对象还没有创建完成,但是因为编译器的优化,_instance已经不再是空了,这时,线程A被弹出,线程B进入,线程B发现实例不为空,直接返回实例,但是线程B并不知道,这个实例还没有创建完成,接下来线程B就是用这个没有完成创建的实例,进行各种操作,危险性不言而喻了吧。
        很多人喜欢用延迟创建SingleTon,但是,我个人觉得这种SingleTon需要对线程有一定的了解,而且,在基于.net的程序上,这个延迟似乎并不是非常重要,所以,我更倾向于最简单的方式,直接在静态字段上创建对象,这样可以轻松的规避锁和创建出多个实例,这是CLR级别保证的事情(如果CLR多次执行了,唯一的解释是,这时在多个AppDomain中执行的)。
        当然,这么做的缺点就是不是足够的延迟,但是,很多场合下,这个方式已经足够的延迟了。
        .net下什么时候会跑类型构造?可以肯定的回答,当第一次使用这个类型的时候。如果,这个单例类型中唯一对外公开的静态成员就是这个GetInstance方法或Instance属性,那么除了这个方法或属性被调用外,还有什么可以导致这个类型的构造被创建哪?
        所以,不需要太在意延迟创建,别忘了,写的代码越多,可能会出现的Bug也越多,既然在字段上直接创建没有什么太大的问题,为什么不这么用哪?
        当然,这里所说的SingleTon都是AppDomain级别的,如果要让多个AppDomain之间公用一个实例,那就不能这么简单的事情了。

  • 相关阅读:
    How to convert VirtualBox vdi to KVM qcow2
    (OK)(OK) adb -s emulator-5554 shell
    (OK)(OK) using adb with a NAT'ed VM
    (OK) How to access a NAT guest from host with VirtualBox
    (OK) Creating manually one VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK)(OK) Creating VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK) Creating_VMs_from_an_existing_VDI_file.txt
    (OK) Creating VMs from an existing VDI file —— in OS X
    (OK) install_IBM_SERVER.txt
    (OK) install chrome & busybox in android-x86_64 —— uninstall chrome
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/781655.html
Copyright © 2011-2022 走看看