本篇内容介绍了“web设计模式中的单例模式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
单例模式 (Singleton Pattern)使用的比较多,比如我们的 controller 和 service 都是单例的,但是其和标准的单例模式是有区别的。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式的结构很简单,只涉及到一个单例类,这个单例类的构造方法是私有的,该类自身定义了一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
单例模式分为懒汉单例和饿汉单例;饿汉单例代码很简单,顾名思义,饿汉单例就是类初始化的时候就将该单例创建,示例代码如下:
public class Singleton { private static final Singleton singleton = new Singleton(); //限制产生多个对象 private Singleton(){ } //通过该方法获得实例对象 public static Singleton getSingleton(){ return singleton; } //类中其他方法,尽量是 static public static void doSomething(){ } }
但是懒汉单例就不那么简单了,懒汉单例是在访问这个类的实例的时候先判断这个类的实例是否创建好了,如果没创建好就要先创建这个单例。也就是说懒汉单例是第一次访问的的时候创建单例,而不是初始化阶段。这将会导致一个问题,如果在多线程场景下,多个线程同时访问这个单例都发现其未被创建,那么这些线程就会分别创建实例,那么这个单例模式就不那么单例了——实例被多次创建。在阿里开发手册中有两条就是和懒汉单例相关的,告诉我们要如何去避免这种情况,第六节的第一条 和第十二条:
(六)并发处理
1.【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
说明:资源驱动类、工具类、单例工厂类都需要注意。
【推荐】在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优
化问题隐患,推荐解
决方案中较为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型。
反例:
class Singleton { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } // other methods and fields... }
volatile
关键字的作用和双重检查锁在我以往的博客中介绍过,文章地址https://mp.weixin.qq.com/s/r52hmD71TtiJjlOzQUvRlA
这篇博客介绍了并发的一些知识,小伙伴有空可以读一读。在这里 volatile
关键字的作用就是保证数据的可见性,双重检查锁是提高代码性能。下面我们分析一下手册中的反例:
其中它的双重检测锁指的是这段代码:
if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); }
这里如果不用双重检测锁的话只能在整个 getHelper
方法上上锁,因为这个方法必须要保证在并发情况下只有一个线程会执行helper = new Helper();
,这段代码。也就是说代码 会成为这样:
public synchronized Helper getHelper() { if (helper == null) { if (helper == null) helper = new Helper(); } return helper; }
整个方法上锁性能明显是不好的,锁的粒度变大了;双重检查锁里面为什么要做两次 if 判断呢,这个问题留给读者思考,并不是特别难的问题。但是反例里面没有考虑到可见性的问题——假设a线程和b线程同时访问 getHelper
方法,然后 b 线程被阻塞住,a线程发现helper
未被实例化,于是执行new方法,然后释放锁;此时b线程进来,或许我们直观的感受是b线程发现属性被实例化直接返回helper
,但实际上不是,当一个线程修改了线程共享的公共资源的时候(此处是helper属性)其他线程未必会被通知到属性被修改,因此b线程有可能发现 helper
还是null 也有可能b线程知道 helper 被赋值了。使用volatile
就可以避免这种情况的发生。因此正确的代码应该是这样的:
class Singleton { private volatile Helper helper = null; public Helper getHelper() { ······ } // other methods and fields... }
“web设计模式中的单例模式是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。