设计模式--01.单例模式
在程序设计中,需要保证一个只有一个对象实例,就是所谓的单例模式。在java中,有很多单例模式的实现,这篇博客是对这几种单例模式的优缺点进行分析和优化:
饿汉模式
所谓饿汗模式,就是优先创建对象,对象在类加载的时候就已经创建好了,具体的代码如下:
1 | private static Singleton01 instance = new Singleton01(); |
饿汗模式优点是代码简单,由于是在类加载的时候就已经创建好了对象,所以不存在线程安全的问题。
缺点是:有的类在没有使用的时候就已经创建了对象,产生了多余的垃圾对象。
懒汉模式
懒汉模式是针对饿汉模式
的一个优化,类只有在使用的时候才会进行实例化:
1 | private static Singleton02 instance; |
懒汉模式的实例在使用的时候才去创建,但是创建的过程是线程不安全的,在多线程的环境下存在多个实例的现象。
double check模式
doublie check模式优化了懒汉模式的线程安全的问题,使用锁来保证当前只有一个线程创建实例。
1 | private static Singleton03 singleton03; |
上面的代码足够保证了线程的安全,但是多线程的场景下会存在一个线程工作,其他线程盲等的现象,所以再为了优化这个问题,再创建进入锁之前再次进行检查, 使用volatile
关键字保证变量在多线程中是共享的,代码如下:
1 | private volatile static Singleton05 singleton05; |
虽然doublecheck的模式,继承了懒汉模式的优点,也不会有线程安全的问题,但还是会存在线程盲等的现象,优化只能降低发生的概率,无法完全避免。
内部类模式
内部类模式是一个比较精妙的设计,是利用了Jvm的类的加载的特点,具体代码如下:
1 |
|
只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance ,只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。目前此方式是所有单例模式中最推荐的模式。
缺点是会产生多余的类,反射也可以破解,通过构造函数可以防止破解。
枚举单例模式
枚举是《effective java》中推荐的单例模式,利用jvm的枚举类无法被反射的创建实例的特点,所以枚举类相对更加安全和健壮。具体的代码如下:
1 | public enum Singleton07 { |
虽然枚举模式有点很多,但是再实际开发中却用的很少,原因是枚举类的用法比较单一(不支持继承)。
使用memoize创建单例
使用Guava
的memoize
函数的特性, 只再第一此获取的时候才调用,可以用来实现单例模式,代码如下:
1 | private static final Supplier<Singleton08> InstanceSuppler = Suppliers.memoize(Singleton08::new); |
使用memoize函数,可以支持多种的实例函数的调用方式,也可以制定方法的过期的时间,更为灵活,个人推荐使用memoize
方式