Sunny公司开发人员使用单例模式实现了负载均衡器的设计,可是在实际使用中出现了一个很是严重的问题,当负载均衡器在启动过程当中用户再次启动该负载均衡器时,系统无任何异常,但当客户端提交请求时出现请求分发失败,经过仔细分析发现原来系统中仍是存在多个负载均衡器对象,致使分发时目标服务器不一致,从而产生冲突。为何会这样呢?Sunny公司开发人员百思不得其解。html
如今咱们对负载均衡器的实现代码进行再次分析,当第一次调用getLoadBalancer()方法建立并启动负载均衡器时,instance对象为null值,所以系统将执行代码instance= new LoadBalancer(),在此过程当中,因为要对LoadBalancer进行大量初始化工做,须要一段时间来建立LoadBalancer对象。而在此时,若是再一次调用getLoadBalancer()方法(一般发生在多线程环境中),因为instance还没有建立成功,仍为null值,判断条件(instance== null)为真值,所以代码instance= new LoadBalancer()将再次执行,致使最终建立了多个instance对象,这违背了单例模式的初衷,也致使系统运行发生错误。java
如何解决该问题?咱们至少有两种解决方案,在正式介绍这两种解决方案以前,先介绍一下单例类的两种不一样实现方式,饿汉式单例类和懒汉式单例类。编程
1.饿汉式单例类安全
饿汉式单例类是实现起来最简单的单例类,饿汉式单例类结构图如图3-4所示:服务器
2.懒汉式单例类与线程锁定多线程
除了饿汉式单例,还有一种经典的懒汉式单例,也就是前面的负载均衡器LoadBalancer类的实现方式。懒汉式单例类结构图如图3-5所示:并发
假如在某一瞬间线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能经过instance == null的判断。因为实现了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例建立代码,线程B处于排队等待状态,必须等待线程A执行完毕后才能够进入synchronized锁定代码。但当A执行完毕时,线程B并不知道实例已经建立,将继续建立新的实例,致使产生多个单例对象,违背单例模式的设计思想,所以须要进行进一步改进,在synchronized中再进行一次(instance == null)判断,这种方式称为双重检查锁定(Double-Check Locking)。使用双重检查锁定实现的懒汉式单例类完整代码以下所示:负载均衡
须要注意的是,若是使用双重检查锁定来实现懒汉式单例类,须要在静态成员变量instance以前增长修饰符volatile,被volatile修饰的成员变量能够确保多个线程都可以正确处理,且该代码只能在JDK 1.5及以上版本中才能正确执行。因为volatile关键字会屏蔽Java虚拟机所作的一些代码优化,可能会致使系统运行效率下降,所以即便使用双重检查锁定来实现单例模式也不是一种完美的实现方式。 函数
|
3.饿汉式单例类与懒汉式单例类比较
饿汉式单例类在类被加载时就将本身实例化,它的优势在于无须考虑多线程访问问题,能够确保实例的惟一性;从调用速度和反应时间角度来说,因为单例对象一开始就得以建立,所以要优于懒汉式单例。可是不管系统在运行时是否须要使用该单例对象,因为在类加载时该对象就须要建立,所以从资源利用效率角度来说,饿汉式单例不及懒汉式单例,并且在系统加载时因为须要建立饿汉式单例对象,加载时间可能会比较长。
懒汉式单例类在第一次使用时建立,无须一直占用系统资源,实现了延迟加载,可是必须处理好多个线程同时访问的问题,特别是当单例类做为资源控制器,在实例化时必然涉及资源初始化,而资源初始化颇有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,须要经过双重检查锁定等机制进行控制,这将致使系统性能受到必定影响。
【做者:刘伟 http://blog.csdn.net/lovelion】
public class Singleton //: IDisposable { private Singleton() { } private static Singleton _Singleton = null; private static object Singleton_Lock = new object(); public static Singleton CreateInstance() { if (_Singleton == null)//保证初始化以后,再也不等待锁 { lock (Singleton_Lock)//保证单线程进入 { Console.WriteLine("进入锁"); if (_Singleton == null)//保证只初始化一次 { _Singleton = new Singleton(); } } } return _Singleton; } }