Java设计模式-单例模式的7种写法详解(下)

Java设计模式-单例模式的7种写法详解(下)

在上文Java设计模式-单例模式的7种写法详解(上)记录的单例模式的,2种懒汉写法,2种饿汉写法,还有一种面试可能会问的错误的饿汉写法。本文继续记录后面三种都值得推荐使用的单例模式写法:双重检查单例模式,静态内部类单例模式,枚举单例模式。
笔记参考的开放视频资源:B站尚硅谷官方—尚硅谷图解Java设计模式韩顺平老师2019力作

6.双重检查实现单例模式

前期知识:

volatile关键字:可理解为一个轻量级的synchronized ;语法上volatile只能用于修饰变量,可以使变量的修改立即更新到主存。

即:volatile保证了不同线程对该变量进行操作的可见性,即一个线程修改了,另一个线程立即可见

可利用这一特性配合同步代码块来实现单例模式

6.1实现步骤

  1. 使用volatile关键字修饰 类的对象(也是私有静态变量)
  2. 私有化构造函数
  3. 提供对外暴露的用于获取实例的静态方法getInstance,该方法中判断实例instance是否为空,为空后的代码块为同步代码块,在同步代码块中再进行以此判断实例instance是否为空(其中实例instance被volatile关键字修饰)。

6.2具体编码

代码分析:双重检查单例模式是如何做到线程安全的呢?此外还有什么优点?

在多线程下,若多个线程同时获取实例,多个线程都进入了第一层if语句中if (instance == null) {},之后由于是同步代码块,只有一个线程进入,且instance变量被volatile关键字修饰,所以第一个线程将instance实例化后会立即更新到主存(因为volatile可以使变量的修改立即更新到主存),所以之后线程进入同步代码块后if (instance == null)雕件判断不成立,直接执行后面的 return instance;返回实例。

此外,在这种方式下instance实例化只执行一次,后面再调用getInstance方法时会直接返回instance。同样也是在调用getInstance方法时才会实例化的(需使用时才实例化)。因此,双重检查单例模式是有懒加载的,线程安全的,效率高的。

/**
 * 6.单例模式 双重检查锁
 * 线程安全 效率高
 */
public class DoubleCheckSingleton {
    // 1.volatile关键字修饰 类的对象(也是私有静态变量)
    //保证内存可见性
    //保证了不同线程对该变量进行操作的可见性,即一个线程修改了,另一个线程立即可见
    //可理解为一个轻量级的synchronized ;语法上volatile只能用于修饰变量
    private static volatile DoubleCheckSingleton instance;

    // 2.私有化构造函数
    private DoubleCheckSingleton() {
        //...
    }

    // 3.第二次判断为同步代码块,配合volatile修饰的变量instance以此实现双重检查
    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

6.3测试验证

测试编码:

//单例模式测试类
public class SingleTest {
    public static void main(String[] args) {
        //测试6  6.单例模式 双重检查锁
        DoubleCheckSingleton doubleCheckSingleton = DoubleCheckSingleton.getInstance();
        DoubleCheckSingleton doubleCheckSingleton2 = DoubleCheckSingleton.getInstance();
        //判断这两实例 是否为同一个
        System.out.println(doubleCheckSingleton == doubleCheckSingleton2);
    }
}

测试结果

true

6.4阶段小结

优点:有懒加载的,线程安全的,效率高的(在第2点具体编码中有过代码分析)。

实际开发推荐使用!

7.静态内部类实现单例模式

前期知识:

  • 类装载时,该类里面的静态内部类不会被装载。
  • 类装载时,线程是安全的。
  • 静态变量只在第一次加载类的时候初始化

7.1实现步骤

  1. 私有化构造函数
  2. 定义一个静态内部类,包含一个变量为StaticInnerClassSingleton类的对象
  3. 对外暴露获取实例的静态方法,返回静态内部类的变量(StaticInnerClassSingleton类的对象)

7.2具体编码

充分利用了前期知识的那几个机制:

因为类装载时该类里面的静态内部类不会被装载,所以StaticInnerClassSingleton装载是,它的静态内部类SingletonInstance不会被装载,所以里面的变量不会被实例化,以此达到了实例懒加载的目的。

因为类装载时线程是安全的,这个JVM机制保证了在初始化实力的时候只有一个线程。所以在多线程下,不会出现多个线程同时调用getInstance()方法生成多个实例的情况。

静态变量只在第一次加载类的时候初始化,因此静态内部类的静态变量INSTANCE自始至终只会初始化一次,达到了单例的效果。

/**
 * 6.静态内部类
 * <p>
 * 使用了以下JVM机制:
 * 1.类装载时,其静态内部类不会被装载
 * 2.类装载时,是线程安全的
 * 3.静态变量只在第一次加载类的时候初始化
 * (优点:效率高;线程安全;懒加载:调用getInstance时静态内部类装载)
 */
public class StaticInnerClassSingleton {
    // 1.私有化构造函数
    private StaticInnerClassSingleton() {
    }

    // 2.静态内部类(含有一个静态变量为StaticInnerClassSingleton类的对象)
    private static class SingletonInstance {
        private static final StaticInnerClassSingleton INSTANCE
                = new StaticInnerClassSingleton();
    }

    // 3.对外暴露获取实例的静态方法
    public static StaticInnerClassSingleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

7.3测试验证

//单例模式测试类
public class SingleTest {
    public static void main(String[] args) {
        //测试7   7.静态内部类单例模式
        StaticInnerClassSingleton staticInnerClassSingleton = StaticInnerClassSingleton.getInstance();
        StaticInnerClassSingleton staticInnerClassSingleton2 = StaticInnerClassSingleton.getInstance();
        //判断这两实例 是否为同一个
        System.out.println(staticInnerClassSingleton == staticInnerClassSingleton2);
    }
}

7.4阶段小结

优点:懒加载,线程安全,效率高(在第2点具体编码中有过代码分析)。

推荐使用!

8.枚举实现单例模式

8.1实现步骤

私有化构造函数

8.2具体编码

/**
 * 8.枚举单例模式
 */
enum EnumSingleton {
    INSTANCE;
    public void methodDemo(){
        //该类的方法demo
        System.out.println("这是使用了枚举单例模式的类的demo方法");
    }
    //其他方法...
}

8.3测试验证

测试编码:

package DesignPattern.SingletonPattern;

//单例模式测试类
public class SingleTest {
    public static void main(String[] args) {
        //测试8 8.枚举单例模式
        EnumSingleton enumSingleton=EnumSingleton.INSTANCE;
        EnumSingleton enumSingleton2=EnumSingleton.INSTANCE;
        //判断这两实例 是否为同一个
        System.out.println(enumSingleton == enumSingleton2);
        enumSingleton.methodDemo();//调用demo方法
    }
}

测试输出:

true
这是使用了枚举单例模式的类的demo方法

8.4阶段小结

优点:写法极其简单,线程安全,还可防止反序列化来创建新的对象

推荐使用!

Java设计模式-单例模式的7种写法 的总结笔记就到此结束了!
笔记参考的开放视频资源:B站尚硅谷官方—尚硅谷图解Java设计模式韩顺平老师2019力作

发布了67 篇原创文章 · 获赞 32 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42391904/article/details/104115054