为什么静态内部类的单例模式是最推荐的?
如何在反射的情况下保证单例?
如何在反序列化中保证单例?
针对上述三个问题有了这篇文章,以一种循序渐进的方式,引出最后一种单例设计模式,希望对大家能够有所帮助。
单例设计模式1、饿汉式
这种其实大家都懂,不多说,上代码。
package singleton; public class Singleton1 { private static Singleton1 instance = new Singleton1(); private Singleton1 (){} public static Singleton1 getInstance() { return instance; } }
优点就是线程安全啦,缺点很明显,类加载的时候就实例化对象了,浪费空间。于是乎,就提出了懒汉式的单例模式
2、懒汉式
(1)懒汉式v1
public class LazySingleton1 { private static LazySingleton1 instance; private LazySingleton1 (){} public static LazySingleton1 getInstance() { if (instance == null) { instance = new LazySingleton1(); } return instance; }
然而这一版线程是不安全的,于是乎为了线程安全,就在getInstance()方法上加synchronized修饰符,于是getInstance()方法如下所示public static synchronized LazySingleton1 getInstance() { if (instance == null) { instance = new LazySingleton1(); } return instance; }
然而,将synchronized加在方法上性能大打折扣(syncrhonized会造成线程阻塞),于是乎又提出一种双重校验锁的单例设计模式,既保证了线程安全,又提高了性能。双重校验锁的getInstance()方法如下所示public static LazySingleton1 getInstance() { if (instance == null) { synchronized (LazySingleton1.class) { if (instance == null) { instance = new LazySingleton1(); } } } return instance; }
(2)懒汉式v2
懒汉式v1的最后一个双重校验锁版,不管性能再如何优越,还是使用了synchronized修饰符,既然使用了该修饰符,那么对性能多多少少都会造成一些影响,于是乎懒汉式v2版诞生。不过在讲该版之前,我们先来复习一下内部类的加载机制,代码如下
public class OuterTest { static { System.out.println("load outer class..."); } // 静态内部类 static class StaticInnerTest { static { System.out.println("load static inner class..."); } static void staticInnerMethod() { System.out.println("static inner method..."); } } public static void main(String[] args) { OuterTest outerTest = new OuterTest(); // 此刻其内部类是否也会被加载? System.out.println("===========分割线==========="); OuterTest.StaticInnerTest.staticInnerMethod(); // 调用内部类的静态方法 } }
输出如下load outer class... ===========分割线=========== load static inner class... static inner method
因此,我们有如下结论
加载一个类时,其内部类不会同时被加载。
一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。。
基于上述结论,我们有了懒汉式V2版,代码如下所示
package singleton; public class LazySingleton2 { private LazySingleton2() { } static class SingletonHolder { private static final LazySingleton2 instance = new LazySingleton2(); } public static LazySingleton2 getInstance() { return SingletonHolder.instance; } }
由于对象实例化是在内部类加载的时候构建的,因此该版是线程安全的(因为在方法中创建对象,才存在并发问题,静态内部类随着方法调用而被加载,只加载一次,不存在并发问题,所以是线程安全的)。
扫描二维码关注公众号,回复: 150334 查看本文章另外,在getInstance()方法中没有使用synchronized关键字,因此没有造成多余的性能损耗。当LazySingleton2类加载的时候,其静态内部类SingletonHolder并没有被加载,因此instance对象并没有构建。
而我们在调用LazySingleton2.getInstance()方法时,内部类SingletonHolder被加载,此时单例对象才被构建。因此,这种写法节约空间,达到懒加载的目的,该版也是众多博客中的推荐版本。
ps:其实枚举单例模式也有类似的性能,但是因为可读性的原因,并不是最推荐的版本。
单例模式--内部类
猜你喜欢
转载自blog.csdn.net/zhmi_1015/article/details/80210649
今日推荐
周排行