单例模式
单例模式是比较常用的设计模式之一,单例对象的类必须保证全局只有一个实例存在。其实现方式有很多种,本篇博客主要介绍常见的几种实现方式。
单例模式最基本的原则:保证该类构造方法的访问权限为private。
饿汉模式
饿汉模式是指类初始化的时候先创建好该类实例,获取实例的时候总是返回该实例。
1 |
|
懒汉模式
懒汉模式可以延迟类的初始化,即类加载阶段不进行初始化操作,只在第一次使用单例类的时候进行初始化,主要用于一些类初始化开销比较大的情况。
缺点:多线程情况下需要特殊处理。
1 | public class LazySingleton { |
静态内部类
饿汉模式不能实现延迟加载,占用内存;懒汉模式需要进行线程安全控制,并且影响性能。这时候静态内部类模式提供了另一种比较好的方式。
1 |
|
由于外部类的初始化并不会引发静态内部类SingletonHolder的初始化,所以只有在第一次调用getInstance方法时才会触发静态内部类的初始化,从而完成实例化工程。
枚举类
枚举类是目前比较推荐的实现单例的方案,因为枚举类实现简单,并且可以保证线程安全和序列化的安全。
1 |
|
双检锁
前面提到过懒汉模式可能会遇到线程安全问题,双检索实现方式可以保证线程安全和延迟初始化。但是该模式只能保证在1.5版本之后的jvm上运行的效果。
1 |
|
相比枚举实现方式,双检索复杂一些。其中instance必须要用volatile修饰,此处的volatile并不是为了保证可见性,而是为了禁止重排序。产生新实例的时候一般按照开辟内存空间-创建实例-引用指向实例内存地址,但是重排序可能会将这个顺序改为开辟内存空间-引用指向实例内存地址-创建实例,如果一个线程按照第二种顺序进行操作,最后创建实例出现了问题,没有完成创建对象的工作,下一个线程进行非空检测时发现引用已经指向了新的实例地址,认为初始化工作已完成,直接返回了该引用,但实际上无法使用该实例。所以需要volatile保证类的初始化顺序。
总结
以上就是常见的实现单例模式的方法,但是上述模式并不能完全保证全局只有一个实例,下一篇我们将深入地讲解哪些操作可以破坏单例模式,以及如何防止这些情况的发生。