前言
如果看到标题就能表示赞同的小伙伴,那估摸你也肯定看过Joshua Bloch大神说过的这么一句话:单元素的枚举类型已经成为实现Singleton的最佳方法。我把它翻译成人话就是:实现单例模式的最佳方法是使用枚举。
单例模式
单例模式(Singleton Pattern):确保一个类有且只有一个实例,并提供一个全局访问点。
在开发中,很多时候有一些对象其实我们只需要一个,例如:线程池(threadpool)、缓存(cache)、默认设置、注册表(registry)、日志对象等等,这个时候把它设计为单例模式是最好的选择。
Java中单例模式是一种广泛使用的设计模式,单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间(比如spring管理的无状态bean);还能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。
单例模式7种写法
单例模式的写法非常多,但很多写法存在一些不足,下面以示例的方式加以指出:
1、懒汉(线程不安全):
public class Singleton { private static Singleton instance; private Singleton (){} //私有构造函数 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
这种写法lazy loading(懒加载)很明显,但是一看就知道,存在线程安全问题,所以这种写法是被禁止的。
2、懒汉(线程安全):
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
显然加了个synchronized来保证线程安全,but,效率太低了,毕竟99.99%的情况下是不需要同步的,有点用力过猛。极力不推荐使用
3、饿汉:
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
这种基于classloder机制避免了多线程的同步问题,初始化的时候就给装载了。但是现在,没有懒加载的效果了。这是最简单的一种实现,据我了解绝大部分小伙伴都是这些写单例模式的~
4、饿汉(变种):
public class Singleton { private static Singleton instance = null; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return instance; } }
和上面差不多,都是在本类初始化即实例化instance。
5、静态内部类:
public class Singleton { // 静态内部类 private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
请注意这种方式和上面是存在不一样的地方的 。
据我了解:面试中能答出这种方式以及下面方式,都属加分项
刚分析了方式3、4都没有lazy loading效果。而这种方式Singleton类被装载了,instance不会被立马初始化,因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,显然它达到了`lazy loading效果
6、双重校验锁(懒汉)
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { // 注意此处还得有次判空~ if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
使用到了volatile机制。这个是第二种方式的升级版,俗称双重检查锁定。既保证了效率,又保证了安全。代码稍微复杂点,但显得比较高级~
7、枚举
public enum Singleton { INSTANCE; }
使用枚举方式实现,也是本文的主菜。
这种方式是Effective Java
作者Josh Bloch
提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊。
所以这种写法,是十分推荐的且是最优的