初学设计模式【7】单例模式——sington
有一些对象其实我们只需要一个,比如:线程池、缓存、充当显卡等设备的驱动程序的对象。这类对象只能有一个实例,否则会出现很多问题,资源消耗过多、数据不一致等。
定义
确保一个类只有一个实例,并提供一个全局的访问点。
类图
说明:1.一个private static的Singleton实例变量instance.
2.一个public static 的getInstance方法,返回instance。
3.一个私有构造方法
经典实现
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
分析:为了保证类只有一个实例,Singleton类的构造方法需为private的这样外界不能通过new直接实例化对象,而只能通过Singleton提供的全局访问点getinstance方法获得,这样就保证了类对象的惟一性。
问题:这样写看上去很完美,实事上是这样吗?可,现实是残酷的,呵呵。
假设,我们有两个线程A,B同时访问上面的代码。假设A线程执行完if(instance == null)后时间片用完,然后B线程来访问上面的代码,假设B线程执行完了instance = new Singleton();即现在Singleton已经有了一个实例化对象。然后B时间片用完A线程回来接着执行,A线程要执行instance = new Singleton();这样就会带来一个问题,Singleton被实例化了两次。显然,这不是我们想要的,应该怎么解决呢?
解决方案:
我们知道上面的问题是由线程同步问题造成的,那么我们可以直接将getinstance方法加上线程同步锁既可,如下:
1 public static synchronized Singleton getInstance() { 2 if (instance == null) { 3 instance = new Singleton(); 4 } 5 return instance; 6 }
新的问题:这样的确是没同步问题了,但线程同步会造成性能的降低,并且对于我们这种情况,我们实际上只需要在第一次进行同步(instance为null时),以后都不需要进行同步了,可我们上面的代码每次执行都会进行同步,显然这是不合理的,那应该怎么解决呢?
方案A:提前初始化instance。
如果我们的程序总是创建并使用单例实例,或者在创建和运行方面的负担不太繁重,你可以提前实例化instance,如下所示:
1 public class Singleton { 2 private static Singleton instance = new Singleton(); 3 4 private Singleton() { 5 } 6 7 public static synchronized Singleton getInstance() { 8 return instance; 9 } 10 11 }
利用此方法,我们可以在JVM加载这个类时就创建此惟一的对象。JVM保证在任何线程访问instance之前,一定先创建此实例。
方案B:双重检查锁
1 public static Singleton getInstance() 2 { 3 if (instance == null) 4 { 5 synchronized(Singleton.class) { 6 if (instance == null) 7 instance = new Singleton(); 8 } 9 } 10 return instance; 11 }
通过此方法既可保证只在instance为null的时候才进行同步,保证了性能。