【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 枚举饿汉式单例
相关推荐
yyyyy_abc34 分钟前
ceph学习笔记
笔记·ceph·学习
晓梦林41 分钟前
ximai靶场学习笔记
android·笔记·学习
IT19951 小时前
Dify笔记-创建自定义工具连接到自建的OpenAPI Schema服务
笔记
一直不明飞行1 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
REDcker1 小时前
有限状态机与状态模式详解 FSM建模Java状态模式与C++表驱动模板实践
java·c++·状态模式
一口吃俩胖子2 小时前
【脉宽调制DCDC功率变换学习笔记021】时域性能准则
笔记·学习
你的保护色2 小时前
【无标题】
java·服务器·网络
basketball6162 小时前
C++ 构造函数完全指南:从入门到进阶
java·开发语言·c++
starvapour2 小时前
SSH笔记
笔记·ssh
淘矿人2 小时前
Claude辅助DevOps实践
java·大数据·运维·人工智能·算法·bug·devops