【Java】单例模式

单例模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

单例模式包含懒汉式和饿汉式,运行有且仅有一个实例化对象,只会new一次,两者区别在于何时new一个对象

原理:

如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

实例化对象的创建要消耗大量的时间和资源

在整个软件系统运行过程中,这个类只被实例化一次,以后不论在哪都只调用这一个实例

要求:

  • 掌握五种单例模式的实现方式
  • 理解为何 DCL 实现时要使用 volatile 修饰静态变量
  • 了解 jdk 中用到单例的场景

饿汉式

在类加载之后先通过new关键字创建一个对象,后续调用getInstance()方法时直接返回该对象

java 复制代码
public class Singleton implements Serializable {
    // 构造方法私有化,不能通过new关键字来创建对象
    private Singleton() {
        // 构造方法抛出异常是防止反射破坏单例
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton()");
    }

    // 私有,静态,不可变
    private static final Singleton INSTANCE = new Singleton();

    public static Singleton getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

    // 防止反序列化破坏单例
    public Object readResolve() {
        return INSTANCE;
    }
}

枚举饿汉式

枚举饿汉式能天然防止反射、反序列化破坏单例

java 复制代码
public enum Singleton {
    INSTANCE;

    private Singleton() {
        System.out.println("private Singleton()");
    }

    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

懒汉式

在第一次调用getInstance()方法时通过new创建对象,以后再次调用该方法时,直接返回第一次调用时创建的对象

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static Singleton INSTANCE = null;

    // 同步执行,避免线程问题
    public static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

}

其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步,因此有了下面的双检锁改进

双检锁懒汉式

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static volatile Singleton INSTANCE = null; // 可见性,有序性

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

为何必须加 volatile:

  • INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造
  • 如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象

内部类懒汉式

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static class Holder {
        static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
  • 避免了双检锁的缺点

实例

JDK 中单例的体现

  • Runtime 体现了饿汉式单例
  • Console 体现了双检锁懒汉式单例
  • Collections 中的 EmptyNavigableSet 内部类懒汉式单例
  • ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
  • Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例
相关推荐
9号达人16 小时前
Java18 新特性详解与实践
java·后端·面试
我不是混子16 小时前
java浮点数精度问题及解决方案
java·后端
杰尼君16 小时前
STM32CubeMX笔记(11)-- AD模块使用
笔记·stm32·嵌入式硬件
_JinHao16 小时前
Cesium Viewer对象详解——Cesium基础笔记(快速入门)
前端·javascript·笔记·3d·webgl
花心蝴蝶.16 小时前
Java 中的代理模式
java·开发语言·代理模式
Mr_WangAndy16 小时前
C++设计模式_创建型模式_单件模式
c++·单例模式·设计模式
舒克起飞了16 小时前
设计模式——单例模式
java·单例模式·设计模式
Java&Develop17 小时前
GitLab-如何基于现有项目仓库,复制出新的项目仓库
java
一只乔哇噻17 小时前
java后端工程师进修ing(研一版‖day49)
java·开发语言
稻草猫.17 小时前
Java线程安全:volatile与wait/notify详解
java·后端·idea