首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

设计方式之单例模式(Singleton)

2012-06-27 
设计模式之单例模式(Singleton)单例模式:确保一个类只有一个实例,并提供一个全局访问点。?要想保证一个类只

设计模式之单例模式(Singleton)

单例模式:确保一个类只有一个实例,并提供一个全局访问点。

?

要想保证一个类只有一个实例,我们不能将构造方法暴露出去,否则调用方就可能通过你提供的构造方法去实例化该类的实例,这样我们就无法保证该类只有一个实例了。因此,我们不能给类的构造方法赋予public的访问权限。

?

单例模式的实例化分为两种:急切实例化和延迟实例化

?

急切实例化:依赖JVM在加载这个类时马上创建此唯一的单件实例,通常表现为一个静态引用。如果程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,我们可以采取急切实例化的方式创建单件。

public class Product {//静态成员——将在JVM加载该类的时候就完成对象的实例化private static Product product = new Product();private Product(){}public static Product newInstance(){//始终返回被加载时创建的静态成员实例return product;}}
?

急切实例化单例模式UML图:


设计方式之单例模式(Singleton)

延迟实例化:只在真正需要该类的实例的时候才实例化。通常在创建和运行时负担比较重的时候选用此种方案。

public class Product {//静态成员——之前先不去实例化对象private static Product product;private Product(){}public static Product newInstance(){//只有在需要该类的时候调用此方法时才去完成实例化if(null == product){product = new Product();}return product;}}
?

延迟实例化单例模式UML图:


设计方式之单例模式(Singleton)

延迟实例化所面临的线程安全问题:

很多人在使用延迟实例化单例模式时都没有考虑线程安全问题。我们看下面一段代码:

/** * 延迟实例化单例模式 */public class Product {private static Product product;private Product(){}public static Product newInstance() throws Throwable{if(null == product){Thread.sleep(5000);//这时可能会有两个线程同时进入到这里product = new Product();//之后可能会创建两个Product类的实例}return product;}}

?上段代码有时也可能只会创建一个实例,这取决哪个线程抢占到执行权,但不排除创建两个实例的可能性,有兴趣的朋友可以自己多尝试几次,或者借助调试的手段手动切换两个线程的执行顺序,就会出现创建两个实例的情景。既然如此,我们以后就不要像上面的代码这样使用延时实例化。

?

如何解决延迟实例化所面临的线程安全问题:

方法一:使用synchronized,如下所示:

/** * 延迟实例化单例模式 */public class Product {private static Product product;private Product(){}public static synchronized Product newInstance() throws Throwable{if(null == product){product = new Product();}return product;}}
?

上面的代码虽然能解决线程安全问题,但是每次调用newInstance方法时都会被同步,无疑会带来性能损耗,你必须知道同步一个方法可能造成程序执行效率下降100倍。如果你可以接受这样的额外损耗,你大可可以这样来用(即简单又有效),如果你可能需要频繁的调用这个同步方法,又无法接受这样的性能损失,可能就得想其他的办法啦。

?

方法二:使用“双重检查加锁”,如下所示:

/** * 延迟实例化单例模式 */public class Product {private volatile static Product product;//使用volatileprivate Product(){}public static Product newInstance() throws Throwable{if(null == product){synchronized(Product.class){//保护起来if(null == product){//之后再次检查product = new Product();}}}return product;}}
?

这样做比直接使用同步方法带来的损耗要低很多。如果你不想使用synchronized,也可以使用阻塞队列,JDK也是这么推荐的。

?

方法三:使用急切实例化,当然之前介绍了急切实例化也有他的缺点,如果你能接受的话,这也是个简单有效的方案。

?

单例模式请注意多个ClassLoder

每个类加载器都定义了一个名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类被加载多次,如果这样的事情发生在单件上,就会产生多个实例并存的“单件”,所以需要引起注意。

?

参考资料:

Head First 设计模式 (中国电力出版社)

?

?

?

?

?

?

?

?

热点排行