【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 枚举饿汉式单例
相关推荐
三品吉他手会点灯2 小时前
C语言学习笔记 - 20.C编程预备计算机专业知识 - 变量为什么必须的初始化【重点】
c语言·笔记·学习
kobesdu2 小时前
【ROS2实战笔记-12】rosshow:终端里的盲文可视化与无头机器人的现场调试
笔记·机器人·ros·移动机器人
代码AI弗森2 小时前
一文理清楚“算力申请 / 成本测算 / 并发评估”
java·服务器·数据库
sakiko_3 小时前
UIKit学习笔记1-创建项目(使用UIKit)、使用组件
笔记·学习
Old Uncle Tom3 小时前
OpenClaw 记忆系统 -- 记忆预加载
java·数据结构·算法·agent
小小小米粒3 小时前
Collection单列集合、Map(Key - Value)双列集合,多继承实现。
java·开发语言·windows
智者知已应修善业3 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
摇滚侠3 小时前
expdp 查看帮助
java·数据库·oracle
:1214 小时前
java基础
java·开发语言
曹牧5 小时前
Spring:@RequestMapping注解,匹配的顺序与上下文无关
java·后端·spring