单例模式 DCL延迟初始化的不足和改进

如果某个类中包容了多个静态和实例方法,而这个类又频繁地被多个线程访问,则锁定整个类型将有可能带来严重的性能损失,因为所有对此类方法的调用都必须串行执行。

       解决方法是“提供专用的用于线程同步的对象或类型”。

       例如,可以定义一个“没什么用的”类型专用于线程同步:

 class UseOnlyForLock

    {

    }

 

       在需要线程同步的地方这样调用:

 

    lock(typeof(UseOnlyForLock))

    {

          //线程同步代码……

    }

      

       这个方法多用于多线程同步静态方法。

       在包容多个方法的类内部,还可以临时增加专用于同步的“没有什么用”的实例成员,专用于多线程同步实例方法:

 

    class SomeClass

    {

        private object SyncObject=new object();

        void SomeMethod()

        {

            lock(SyncObject)

            {

                //线程同步代码……

            }

        }

    }

 

       综合应用上述两种方法,可以有效地避免锁定一个“拥有很多方法和属性”的类型所带来的性能损失。

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bitfan/archive/2009/12/16/5021214.aspx

Double Check Locking和延迟初始化

实际上,这段代码并非DCL,它比DCL的安全性更弱。它本来是希望延迟实例化对象,并通过sychronized块限制对象只实例化一次。而由于if判断和sychronized块之间不是原子操作,实例化可能会执行多次。再者,仍然是instance = new MemcachedService();这行代码,对MemcachedService实例的创建和对instance变量的写入操作可能会被重排,造成Unsafe Publication。

 

这段代码是用在一个web应用程序中,用来从cache中获取事先存储的数据。在测试环境中一切良好,一部署到线上环境,即发现getValue处无法获取cache值。开始怀疑是环境问题,后来在线上环境同一台机器另部署了一套相同的程序,一切正常。为这个问题几乎折腾了一整天,当最后定位到延迟初始化问题时,原因便显而易见:线程安全缺陷通常在高并发的情况下问题才会显露,测试环境由内部人员使用,访问量很小,也没有进行压力测试,所以问题没有呈现;而线上环境并发访问量很高,程序一上线,问题立马显露无疑。

既然DCL存在线程安全隐患,且一旦出问题又难以调试,所以人们建议永远不要在Java程序中使用DCL。实际上,在整个应用范围内的单例(特别是在服务端程序中),往往没必要采用延迟初始化。延迟初始化主要是为了让一些昂贵的操作,在真正需要时才被执行。对于为大量持续不断的客户请求提供服务的程序来说,对单例的访问总是急切的,所以延迟初始化没有什么实际意义。

上面的代码改写成这样(急切初始化)既简洁又安全,同时消除了同步的开销:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值