[设计模式]Singleton - 单例模式
在写代码的过程中,我们会发现有些对象,其实我们只需要一个实例,比方说:线程池(threadpool)、日志对象等,这时候就需要用到“单例模式”,可以创建唯一的实例。
简单的单例模式的代码如下:
package com.geekyjane.cnblogs.singleton; public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
要满足“只创建一个实例”的条件,就必须禁止其他的类实例化该类,通过自己提供类型实例来控制实例的唯一性。对于有些对象,实例化起来非常耗费资源,而程序在某次执行过程中又有一直没用到它,这样就会造成一种浪费,所以用了lazy instantiaze(延迟实例化),可以在需要的时候再进行实例的创建,从而避免了这样的一种浪费。
但这也造成了一个多线程的问题,当两个线程AB,当A进入getInstance()方法,检测到uniqueInsatance为空,会通过new Singleton()来创建一个新的对象实例,而这时线程B也进入了该方法,发现uniqueInstance还没有创建出来(为空),也做了uniqueInstance = new Singleton()的操作,我们就会得到两个该对象的实例,这不符合单例模式的要求。
所以对于多线程来说,我们要考虑线程安全。有三种方法,第一种是拒绝延迟实例化,第二种是同步该刚发,第三种是双重检查加锁(double-checked locking)。
package com.geekyjane.cnblogs.singleton; public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return uniqueInstance; } }
对于第一种来说,是最简单快捷的,但是必然是没有延迟实例化来得好,所以不推荐。
package com.geekyjane.cnblogs.singleton; public class Singleton2 { private static Singleton2 uniqueInstance; private Singleton2() { } public static synchronized Singleton2 getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton2(); } return uniqueInstance; } }
这种方法快捷有效,但是同步会降低性能,而且每一次取得实例时都要同步,这是一种累赘。所以如果你的应用程序要频繁运行getInstance方法,那还是放弃这个选择吧。
package com.geekyjane.cnblogs.singleton; public class Singleton3 { private volatile static Singleton3 uniqueInstance; private Singleton3() { } public static Singleton3 getInstance() { if (uniqueInstance == null) { synchronized (Singleton3.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton3(); } } } return uniqueInstance; } }
这种方法避免了方法二的缺点,首先检查是否实例已经创建了,如果尚未创建,才进行同步。这样做可以大大减少getInstance的耗时。但是如果你的代码不需要性能上的考虑,这么写似乎又会有些麻烦了。