标签:
对于某些类来说,我们其实只需要有一个实例化的对象。比如:注册表,资源管理器,打印机驱动程序等等。
如果我们保证以上的类只有一个实例,并只提供一个统一的访问点的话。系统中便可以统一管理这个对象。
以上类只有一个实例,同时也可以节约系统资源,保证对象信息的一致性。
我们可以通过单例模式来确保对象的唯一性。
确保某一个类只有一个实例,只提供一个全局访问点,该类自行实例化并向整个系统提供这个实例,这个类就是单例类。
类图:
代码示例(java)
上述的代码示例,在运行多线程的程序中,可能还是存在一些问题,不能保证对象的唯一性。
例如:有线程1和线程2,同时调用Singleton类的Instance方法。线程1中判断_instance字段为null,对_instance字段进行初始化操作。
如果Singleton类的初始化信息量大,初始化时间较长。在线程1初始化_instance字段的过程中,假设线程2也调用了Singleton类的Instance方法。如果此时_instance字段还是为null的话,线程2也会对_instance字段进行初始化操作。从而产生了两个Singleton类的实例。Singleton类的实例唯一性无法保证。
对于多线程的问题,我可以使用饿汉和懒汉单例模式来保证对象的唯一性。
解决方案:
1.饿汉式单例
当Singleton类加载时,静态变量_instance会被初始化,此时Singleton类的私有构造函数会被调用,单例类的唯一实例就被创建了。
这样在任何线程调用getInstance()方法之前,Singleton类已经被创建,确保了线程安全。
2.懒汉式单例
第一个_instance == null的判断,是有性能上的好处的。因为只有在第一次_instance == null的时候,才会有对代码进行锁定的操作。_instance不为null的时候,就直接返回了_instance对象了。这样对代码进行锁定的操作只会进行一次了。
如果不进行双重判断,还是可能将会产生多个单例对象,从而违背单例模式的设计思想。
假设有线程A和线程B同时调用了Instance()方法,此时_instance为null,线程A和线程B都能通过instance == null的第一次判断。
由于实现了synchronized加锁机制,线程A和线程B不能同时执行synchronized锁定的代码。
假设线程A先进入synchronized锁定的代码,实例化Singleton对象。而线程B则处于排队等待状态,它必须等待线程A执行完毕后,才可以进入synchronized锁定的代码中。
但当线程A实例化完毕后,此时线程B并不知道Singleton对象已经被线程A实例化完毕了。所以必须在synchronized锁定的代码中加上第二个_instance==null的判断。不然线程B将继续创建新的实例,从而导致产生两个单例对象。
3.饿汉式单例 vs 懒汉式单例
饿汉式单例
优点:
饿汉式单例在类加载的时候就被实例化,无需考虑多线程的问题。代码也不需要锁定,性能上有一定的优势。
缺点:
不管系统中是否要引用实例,饿汉式单例在类加载的时候都会创建对象。如果此时系统不需要引用该实例,这样就会造成系统资源的浪费。
懒汉式单例
优点:
懒汉式单例实现了延迟加载,在类需要被使用时,才会被实例化,不会一直占用系统资源。
缺点:
要处理好多线程同时访问的问题,就需要锁定代码,考虑多个线程的同步,这样就会对系统的性能造成一定的影响。
单例模式是一种比较简单的创建型模式,在某种程度上来说它是限制了而不是促进了类的创建。保证类只有一个实例,并只提供一个全局的访问点。在很多应用软件中都有广泛的应用。
标签:
原文地址:http://www.cnblogs.com/YaoxTao/p/4931895.html