单例模式的几种实现方式

单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局状态或者希望整个应用程序中某个对象只有一个实例时非常有用。

单例模式的关键特点包括:

  1. 唯一实例:单例类通过私有构造函数和静态实例变量确保只有一个实例被创建。
  2. 全局访问点:单例类提供一个公共的静态方法,允许其他对象获取到这个唯一实例的引用。
  3. 延迟初始化:单例实例可以在第一次使用时创建,也可以在类加载时立即创建。延迟初始化有助于节省资源,因为它允许系统在需要时才创建实例。
  4. 线程安全:在多线程环境中,单例模式需要确保线程安全,避免多个线程同时创建多个实例。

实现方案的话,我这里推荐三种:

1、枚举(推荐)

java 复制代码
public enum Singleton {
    INSTANCE;
    public void doSomething(String str) {
        System.out.println(str);
   }
}

《Effective Java》作者推荐的⼀种单例实现⽅式,简单⾼效,⽆需加锁,线程安全,可以避免通过 反射破坏枚举单例。

2、静态内部类(推荐)

java 复制代码
public class Singleton {
    // 私有化构造⽅法
    private Singleton() {
   }
    // 对外提供获取实例的公共⽅法
    public static Singleton getInstance() {
        return SingletonInner.INSTANCE;
   }
    // 定义静态内部类
    private static class SingletonInner{
        private final static Singleton INSTANCE = new Singleton();
   }
}

当外部类Singleton被加载的时候,并不会创建静态内部类SingletonInner的实例对象。只有当调 ⽤getInstance()⽅法时, SingletonInner才会被加载,这个时候才会创建单例对象INSTANCE 。 INSTANCE的唯⼀性、创建过程的线程安全性,都由 JVM 来保证。 这种⽅式同样简单⾼效,⽆需加锁,线程安全,并且⽀持延时加载。

3、双重校验锁

java 复制代码
public class Singleton {
    private volatile static Singleton uniqueInstance;
    // 私有化构造⽅法
    private Singleton() {
   }
    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进⼊加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
               }
           }
       }
        return uniqueInstance;
   }
}

uniqueInstance采⽤volatile关键字修饰也是很有必要的,uniqueInstance = new Singleton();这段 代码其实是分为三步执⾏: 1. 为uniqueInstance分配内存空间 2. 初始化uniqueInstance 3. 将uniqueInstance指向分配的内存地址 但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不会 出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤getUniqueInstance () 后发现uniqueInstance不为空,因此返回uniqueInstance ,但此时uniqueInstance还未被初始化。

这种⽅式实现起来较麻烦,但同样线程安全,⽀持延时加载。

相关推荐
Lyyaoo.13 分钟前
What is Maven?
java·spring boot·maven
23.19 分钟前
【Java】NIO零拷贝:为何transferTo需要循环调用?
java·面试·nio
I_LPL22 分钟前
day48 代码随想录算法训练营 图论专题1
java·算法·深度优先·图论·广度优先·求职面试
架构师沉默24 分钟前
如果 Spring 没了,Java 会怎么样?
java·后端·架构
叶宇燚33 分钟前
Java整理--数据结构篇
java·开发语言·数据结构
吴声子夜歌1 小时前
小程序——界面API(一)
java·javascript·小程序
予枫的编程笔记1 小时前
【面试专栏|Java并发编程】从Runnable到Callable,Java4种线程创建方式
java·多线程·thread·java面试·runnable·callable·java线程
野犬寒鸦1 小时前
SAP后端实习开发面试:操作系统与网络核心考点及Linux与Redis
java·服务器·网络·后端·面试
ServBay1 小时前
代码减半,10分钟彻底告别 Java 开发旧习
java·后端
MegaDataFlowers1 小时前
CRUD操作及配置解析
java·mybatis