美文网首页JavaJava 杂谈
设计模式一、单例模式

设计模式一、单例模式

作者: Alisallon | 来源:发表于2019-05-12 11:45 被阅读1次

系列传送门
设计模式一、单例模式
设计模式二、简单工厂模式
设计模式三、工厂模式
设计模式四、抽象工厂模式

简单单例(推荐)

public class SimpleSingleton {

    // TODO 第1步:把构造函数私有化,禁止外部使用构造函数创建实例对象
    private SimpleSingleton() {
    }

    // TODO 第2步:定义并初始化私有静态实例变量,在类加载阶段即完成该静态实例的初始化
    private static SimpleSingleton instance = new SimpleSingleton();

    // TODO 第3步:对外提供一个获取私有静态实例方法
    public static SimpleSingleton getInstance() {
        return instance;
    }
}

基于类加载过程进行说明

  1. 加载
    加载SimpleSingleton.class字节码文件到JVM,生成一个SimpleSingleton的java.lang.Class对象(生成在方法区)。
  2. 验证
    验证SimpleSingleton.class字节码中包含的信息是否符合JVN规范,是否会危害JVM自身的安全。
  3. 准备
    为类变量(这里是instance)分配内存(分配在方法区),并设置初始值(这里给instance赋值为null)。
  4. 解析
    把.class字节码文件里常量池中定义的符号引用(CONSTANT_***格式的字面量)转换为直接引用,即把常量直接指向值所在的地址。
  5. 初始化
    执行类构造器<client>方法,完成类变量(这里是instance)的赋值操作(这里是在堆内存上创建SimpleSingleton实例)和执行类静态块(这里没有类静态块)。
    如果没有类变量和类静态块,JVM不会生成类构造器<client>方法。
  6. 使用
  7. 卸载

优点
线程安全。
在JVM启动时的类加载阶段即完成静态实例变量的初始化,不存在多线程争抢创建静态实例的情况。
缺点
对于大量不需要马上使用的单例对象,会在类加载阶段创建大量的实例,占用内存空间。
推荐
如果能确保这个单例一定有使用的机会,也就是说这个单例迟早都要初始化,那么用这个方式也是最简单最直接最好用的。

不安全的延迟初始化单例(不推荐)

public class UnsafeLazySingleton {

    // TODO 第1步:把构造函数私有化,禁止外部使用构造函数创建实例对象
    private UnsafeLazySingleton() {
    }

    // TODO 第2步:定义私有静态实例变量,这里不初始化
    private static UnsafeLazySingleton instance;

    // TODO 第3步:对外提供一个获取私有静态实例方法
    public static UnsafeLazySingleton getInstance() {
        // TODO 第4步:在这里初始化私有静态实例变量
        if (instance == null) {
            instance = new UnsafeLazySingleton();
        }
        return instance;
    }
}

基于类加载过程进行说明

  1. 加载
    加载UnsafeLazySingleton.class字节码文件到JVM,生成一个UnsafeLazySingleton的java.lang.Class对象(生成在方法区)。
  2. 验证
    验证UnsafeLazySingleton.class字节码中包含的信息是否符合JVN规范,是否会危害JVM自身的安全。
  3. 准备
    为类变量(这里是instance)分配内存(分配在方法区),并设置初始值(这里给instance赋值为null)。
  4. 解析
    把.class字节码文件里常量池中定义的符号引用(CONSTANT_***格式的字面量)转换为直接引用,即把常量直接指向值所在的地址。
  5. 初始化
    执行类构造器<client>方法,完成类变量(这里是instance)的赋值操作(这里没有对静态变量赋值,所以不会初始化instance)和执行类静态块(这里没有类静态块)。
    如果没有类变量和类静态块,JVM不会生成类构造器<client>方法。
  6. 使用
    线程调用getInstance()方法,发现instance为null,此时会初始化instance静态变量(这里是在堆内存上创建UnsafeLazySingleton实例)。
  7. 卸载

优点
只在第一次使用单例实例时,才初始化单例实例,节省内存空间。
缺点
线程不安全。
多线程情况下,会出现争抢初始化单例实例的情况,最坏情况是每个线程都创建了一次instance实例,造成每个线程持有的instance实例不一致。
不推荐

安全的延迟初始化单例(不推荐)

public class SafeLazySingleton {

    // TODO 第1步:把构造函数私有化,禁止外部使用构造函数创建实例对象
    private SafeLazySingleton() {
    }

    // TODO 第2步:定义私有静态实例变量,这里不初始化
    private static SafeLazySingleton instance;

    // TODO 第3步:对外提供一个获取私有静态实例方法,该接口使用synchronized关键字,保证多线程并发情况下只有一个线程能执行该方法
    public synchronized static SafeLazySingleton getInstance() {
        // TODO 第4步:在这里初始化私有静态实例变量
        if (instance == null) {
            instance = new SafeLazySingleton();
        }
        return instance;
    }
}

基于类加载过程进行说明

  1. 加载
    加载SafeLazySingleton.class字节码文件到JVM,生成一个SafeLazySingleton的java.lang.Class对象(生成在方法区)。
  2. 验证
    验证SafeLazySingleton.class字节码中包含的信息是否符合JVN规范,是否会危害JVM自身的安全。
  3. 准备
    为类变量(这里是instance)分配内存(分配在方法区),并设置初始值(这里给instance赋值为null)。
  4. 解析
    把.class字节码文件里常量池中定义的符号引用(CONSTANT_***格式的字面量)转换为直接引用,即把常量直接指向值所在的地址。
  5. 初始化
    执行类构造器<client>方法,完成类变量(这里是instance)的赋值操作(这里没有对静态变量赋值,所以不会初始化instance)和执行类静态块(这里没有类静态块)。
    如果没有类变量和类静态块,JVM不会生成类构造器<client>方法。
  6. 使用
    线程调用getInstance()方法时,先获取当前类的java.lang.Class实例的锁。
    如果拿到了锁,执行到getInstance()方法内部,发现instance为null,此时会初始化instance静态变量(这里是在堆内存上创建SafeLazySingleton实例)。
    如果没拿到锁,则阻塞等待。
  7. 卸载

优点1
只在第一次使用单例实例时,才初始化单例实例,节省内存空间。
优点2
线程安全。
多线程情况下,只有一个线程能执行getInstance()静态方法。
这里是锁getInstance()静态方法,加锁的对象是当前类的java.lang.Class实例。
缺点
并发情况下,多个线程同时调用getInstance()静态方法时会争抢锁,一次只有一个线程能获得锁,其他线程只能阻塞等待。
每次调用getInstance()静态方法时都要获得锁,执行效率太低
不推荐

双重检测的安全的延迟初始化单例(不怎么推荐)

public class DoubleCheckSafeLazySingleton {

    // TODO 第1步:把构造函数私有化,禁止外部使用构造函数创建实例对象
    private DoubleCheckSafeLazySingleton() {
    }

    // TODO 第2步:定义私有静态实例变量,这里不初始化
    private static DoubleCheckSafeLazySingleton instance;

    // TODO 第3步:对外提供一个获取私有静态实例方法
    public static DoubleCheckSafeLazySingleton getInstance() {
        // TODO 第4步:在这里检测私有静态实例变量是否已被初始化
        if (instance == null) {
            // TODO 第5步:还未被初始化,则先给当前类的java.lang.Class实例加锁
            synchronized (DoubleCheckSafeLazySingleton.class) {
                // TODO 第6步:获得锁之后,在这里再次检测私有静态实例变量是否已被初始化
                if (instance == null) {
                    // TODO 第7步:还未被初始化,则在这里初始化私有静态实例变量
                    instance = new DoubleCheckSafeLazySingleton();
                }
            }
        }
        return instance;
    }
}

基于类加载过程进行说明

  1. 加载
    加载DoubleCheckSafeLazySingleton.class字节码文件到JVM,生成一个DoubleCheckSafeLazySingleton的java.lang.Class对象(生成在方法区)。
  2. 验证
    验证DoubleCheckSafeLazySingleton.class字节码中包含的信息是否符合JVN规范,是否会危害JVM自身的安全。
  3. 准备
    为类变量(这里是instance)分配内存(分配在方法区),并设置初始值(这里给instance赋值为null)。
  4. 解析
    把.class字节码文件里常量池中定义的符号引用(CONSTANT_***格式的字面量)转换为直接引用,即把常量直接指向值所在的地址。
  5. 初始化
    执行类构造器<client>方法,完成类变量(这里是instance)的赋值操作(这里没有对静态变量赋值,所以不会初始化instance)和执行类静态块(这里没有类静态块)。
    如果没有类变量和类静态块,JVM不会生成类构造器<client>方法。
  6. 使用
    当前线程调用getInstance()方法,先判断instance是否已被初始化。
    如果instance已被初始化,则无需获得锁,直接返回instance
    如果instance还未被初始化,则先获得当前类的java.lang.Class实例的锁。
    如果没有拿到锁,则当前线程阻塞等待。
    当前线程拿到锁之后,执行到getInstance()方法内部。
    发现instance已被初始化,直接跳出同步块。
    发现instance为null,此时会初始化instance静态变量(这里是在堆内存上创建DoubleCheckSafeLazySingleton实例)。
  7. 卸载

优点1
只在第一次使用单例实例时,才初始化单例实例,节省内存空间。
优点2
线程安全。
多线程情况下,多线程情况下,只有一个线程能获得当前类的java.lang.Class实例的锁并检测和执行初始化单例实例操作。
这里是锁getInstance()静态方法内部的代码块,加锁的对象是当前类的java.lang.Class实例。
当私有静态实例已被初始化时,所有线程将不必去获得锁。
只有当私有静态实例未被初始化时,才会去获得锁,极大的提高了执行效率。
既保证了单例实例的延迟初始化,又兼顾了线程安全。
缺点
这个方式的写法比较繁琐,如果这个单例迟早都要使用,还不如使用SimpleSingleton的方式。
不怎么推荐

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

  • python 单例

    仅用学习参考 目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计...

  • 基础设计模式:单例模式+工厂模式+注册树模式

    基础设计模式:单例模式+工厂模式+注册树模式 单例模式: 通过提供自身共享实例的访问,单例设计模式用于限制特定对象...

网友评论

    本文标题:设计模式一、单例模式

    本文链接:https://www.haomeiwen.com/subject/vptcaqtx.html