Linux安全网 - Linux操作系统_Linux 命令_Linux教程_Linux黑客

会员投稿 投稿指南 本期推荐:
搜索:
您的位置: Linux安全网 > Linux集群 > Architecture > » 正文

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

来源: kt8668 分享至:

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

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

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

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

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

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


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

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

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


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

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

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

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

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

参考资料:

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

  • 大小: 3.5 KB
  • 大小: 3.2 KB
  • 查看图片附件

Tags:
分享至:
最新图文资讯
1 2 3 4 5 6
验证码:点击我更换图片 理智评论文明上网,拒绝恶意谩骂 用户名:
关于我们 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 发展历史