创建型模式-单例模式

饿汉式创建

该方式在类加载时就会创建该单实例对象。

  1. 静态成员变量
java 复制代码
package per.mjn.pattern.singleton.demo1;

/**
 * 饿汉式:静态成员变量
 */
public class Singleton {
    // 1. 私有构造方法
    private Singleton() {}

    // 2. 创建静态成员变量
    private static Singleton instance = new Singleton();

    // 3. 提供一个静态方法,返回静态成员变量
    public static Singleton getInstance() {
        return instance;
    }
}
java 复制代码
package per.mjn.pattern.singleton.demo1;

public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

测试输出

bash 复制代码
true
  1. 静态代码块
java 复制代码
package per.mjn.pattern.singleton.demo2;

/**
 * 饿汉式:静态代码块
 */
public class Singleton {
    // 1. 私有构造方法
    private Singleton() {}

    // 2. 声明Singleton类型的变量
    private static Singleton instance;  // null

    // 3. 在静态代码块中创建Singleton类型的对象
    static {
        instance = new Singleton();
    }

    // 4. 提供一个静态方法,返回Singleton类型的对象
    public static Singleton getInstance() {
        return instance;
    }
}
java 复制代码
package per.mjn.pattern.singleton.demo2;

public class Client {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

测试输出

bash 复制代码
true
  1. 枚举方式创建

枚举类实现单例模式借助枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯-一种不会被破坏的单例实现模式。

java 复制代码
package per.mjn.pattern.singleton.demo6;

/**
 * 枚举实现方式
 */
public enum Singleton {
    INSTANCE;
}
java 复制代码
package per.mjn.pattern.singleton.demo6;

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);
    }
}

测试结果

bash 复制代码
true

懒汉式创建

类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。

java 复制代码
package per.mjn.pattern.singleton.demo3;

/**
 * 懒汉式
 */
public class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 声明Singleton类型的变量instance
    private static Singleton instance;

    // 获取唯一可用的对象
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
java 复制代码
package per.mjn.pattern.singleton.demo3;

public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

测试输出

bash 复制代码
true

双重检查锁方式

java 复制代码
package per.mjn.pattern.singleton.demo4;

/**
 * 双重检查锁方式
 */
public class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 声明Singleton类型的变量instance
    private static Singleton instance;

    // 获取唯一可用的对象
    public static Singleton getInstance() {
        // 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回instance
        if (instance == null) {
            synchronized (Singleton.class) {
                // 第二次判断,如果instance的值不为null,不需要抢占锁,直接返回instance
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双重检查锁模式在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用volatile 关键字,volatile 关键字可以保证可见性和有序性

java 复制代码
package per.mjn.pattern.singleton.demo4;

/**
 * 双重检查锁方式
 */
public class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 声明Singleton类型的变量instance
    private static volatile Singleton instance;

    // 获取唯一可用的对象
    public static Singleton getInstance() {
        // 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回instance
        if (instance == null) {
            synchronized (Singleton.class) {
                // 第二次判断,如果instance的值不为null,不需要抢占锁,直接返回instance
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

添加volatile关键字之后的双重检査锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

静态内部类方式

静态内部类单例模式中实例由内部类创建,由于在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

java 复制代码
package per.mjn.pattern.singleton.demo5;

/**
 * 静态内部类方式
 */
public class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 定义一个静态内部类
    private static class SingletonHolder {
        // 在内部类中声明并初始化外部类的对象
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供公共的访问方式
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

第一次加载singleton类时不会去初始化INSTANCE,只有第一次调用getInstance(),虚拟机加载singletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 singleton 类的唯一性。

静态内部类单例模式是比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

相关推荐
mqiqe10 小时前
AgentScope Java Harness:4. 双层记忆系统 让 Agent 拥有真正的“长期大脑“
java·开发语言
伍一5110 小时前
03-文件管理模块-一个看似简单的模块藏着多少细节
java·ruoyi·若依
gugucoding10 小时前
46. 【Java】JUC并发工具:让并发更简单
java·开发语言
Nebula_g11 小时前
JavaSE基础语法:面向对象高级(代码中的成分)
java·开发语言·编程·javase·技术栈·高级语法
AC赳赳老秦11 小时前
公开图片 OCR 数据提取:OpenClaw 合规采集公开信息图,识别文字与表格并转化为结构化数据
java·大数据·前端·数据库·python·php·openclaw
小强库计算机毕业设计13 小时前
SpringBoot+Vue3 学生宿舍管理系统实战
java·spring boot·后端·vue·学生宿舍管理系统
李冰然13 小时前
深入Java集合框架:HashMap源码解析(JDK 8)
java
人道领域14 小时前
【0-1的agent进阶篇】RAG:从Embedding到检索增强生成的底层逻辑
java·embedding·agent·rag
桦说编程15 小时前
盘点并发集合里那些容易误判的行为
java·后端·性能优化
IT爱学堂16 小时前
java版数据结构和算法+AI算法和技能学习指南
java·开发语言