首页 > 其他分享 >设计模式[1]-单例模式

设计模式[1]-单例模式

时间:2024-08-23 11:04:10浏览次数:10  
标签:singleton getInstance 私有 模式 static private 单例 设计模式 public

代码:https://gitee.com/Aes_yt/design-pattern

单例模式

单例模式(Singleton)是一种创建型设计模式,能够保证一个类只有一个实例,并提供了访问该实例的全局节点。

单例的实现步骤有以下步骤,首先将默认构造函数设置为私有,然后创建一个静态方法来调用私有构造函数来创建对象。

单例类型主要分为懒汉模式和饿汉模式。

  • 饿汉:类加载就会导致单例对象被创建。
  • 懒汉:类加载不会导致单例对象被创建,首次使用该对象时才会创建。

1. 饿汉模式

1.1 静态变量生成
public class HungrySingleton1 {
    // 1. 类中直接创建本类实例
    private static HungrySingleton1 singleton = new HungrySingleton1();

    // 2. 私有构造方法
    private HungrySingleton1() {
    }

    // 3. 公共静态方法,供外部访问
    public static HungrySingleton1 getInstance() {
        return singleton;
    }
}

单元测试通过:

    @Test
    void getInstance() {
        HungrySingleton1 instance1 = HungrySingleton1.getInstance();
        HungrySingleton1 instance2 = HungrySingleton1.getInstance();
        Assertions.assertEquals(instance1, instance2);
    }
1.2 静态代码块生成
public class HungrySingleton2 {
    // 1. 声明静态私有变量,在静态代码块中创建本类实例
    private static HungrySingleton2 singleton;

    static {
        singleton = new HungrySingleton2();
    }

    // 2. 私有构造方法
    private HungrySingleton2() {
    }

    // 3. 公共静态方法,供外部访问
    public static HungrySingleton2 getInstance() {
        return singleton;
    }
}

单元测试通过:

    @Test
    void getInstance() {
        HungrySingleton2 instance1 = HungrySingleton2.getInstance();
        HungrySingleton2 instance2 = HungrySingleton2.getInstance();
        Assertions.assertEquals(instance1, instance2);
    }

饿汉模式是类加载就会创建对象,所以会造成内存浪费。优点是代码简单,而且不用考虑多线程问题。

2. 懒汉模式

2.1 线程不安全
public class LazySingleton1 {
    // 1. 声明静态私有变量
    private static LazySingleton1 singleton;

    // 2. 私有构造方法
    private LazySingleton1() {
    }

    // 3. 公共静态方法,供外部访问,在此处创建对象
    public static LazySingleton1 getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton1();
        }
        return singleton;
    }
}

在getInstance方法中,先判断变量是否存在,不存在的话,就创建对象并返回。但是这种方法是线程不安全的,所以为了保证线程安全,可以给getInstance方法加锁,即2.2的创建方法。

2.2 线程安全,方法加锁
public class LazySingleton2 {
    // 1. 声明静态私有变量
    private static LazySingleton2 singleton;

    // 2. 私有构造方法
    private LazySingleton2() {
    }

    // 3. 公共静态方法,供外部访问,在此处创建对象
    public static synchronized LazySingleton2 getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton2();
        }
        return singleton;
    }
}

跟2.1的代码,主要只有一行有所区别。

public static synchronized LazySingleton2 getInstance()

2.3 线程安全,双重检查锁校验
public class LazySingleton3 {
    // 1. 声明静态私有变量
    private static volatile LazySingleton3 singleton;

    // 2. 私有构造方法
    private LazySingleton3() {
    }

    // 3. 公共静态方法,供外部访问,在此处创建对象
    public static LazySingleton3 getInstance() {
        // 第一次判断,实例不为空,直接返回
        if (singleton == null) {
            synchronized (LazySingleton3.class){
                // 第二次判断
                if(singleton == null){
                    singleton = new LazySingleton3();
                }
            }
        }
        return singleton;
    }
}

2.2的创建方式中,锁直接加在getInstance方法,每次调用此方法都会加锁,过于笨重。

所以改进方式是,利用两次判断,第一次判断,如果实例存在,就返回,那如果不存在,就是得加锁,而第二次判断保证了创建的是单一实例。

仔细观察2.3的代码思路,其实是2.1和2.2的结合。

注意:

singleton 变量使用volatile关键字修饰,是因为多线程情况下,有可能出现空指针异常,因为jvm实例化对象的时候会进行指令优化和指令重排序,加上volatile关键字可以保证可见性和有序性。

2.4 静态内部类实现
public class LazySingleton4 {
    // 1. 私有构造方法
    private LazySingleton4() {
    }

    // 2. 声明静态内部类
    private static class InnerSingleton {
        // 3. 内部类中创建外部类的常量对象
        private static final LazySingleton4 SINGLETON = new LazySingleton4();
    }

    // 4. 公共静态方法,供外部访问,返回静态内部类中创建的常量对象
    public static LazySingleton4 getInstance() {
        return InnerSingleton.SINGLETON;
    }
}

JVM加载外部类的时候,不会加载静态内部类,只有在调用内部类的属性或方法的时候才会加载,而且静态内部类中的属性用static修饰,保证了单例,所以这种方法是线程安全且没有造成性能影响和空间的浪费。

标签:singleton,getInstance,私有,模式,static,private,单例,设计模式,public
From: https://www.cnblogs.com/Aeons/p/18375608

相关文章

  • Scratch编程环境的暗色模式:探索可访问性的边界
    标题:Scratch编程环境的暗色模式:探索可访问性的边界Scratch,这个广受欢迎的图形化编程平台,由麻省理工学院媒体实验室开发,一直致力于为用户提供友好且易于访问的编程体验。随着用户对编程环境个性化需求的增长,Scratch的编程环境是否支持暗模式或可访问性选项,成为了编程教育领......
  • C++设计模式1:单例模式(懒汉模式和饿汉模式,以及多线程问题处理)
    饿汉单例模式        程序还没有主动获取实例对象,该对象就产生了,也就是程序刚开始运行,这个对象就已经初始化了。 classSingleton{public: ~Singleton() { std::cout<<"~Singleton()"<<std::endl; } staticSingleton*get_instance() { return&sin......
  • 火影忍者2——漩涡鸣人(仙人模式)篇
    老规矩,谈火影~火影忍者之——漩涡鸣人(仙人模式)篇众所周知,鸣仙是一个早期的A忍,技能破坏力贼大,一般遇到鸣仙(除非我用了青水+神卡)我是直接退的普攻一技能螺旋丸普通状态下一技能长摁放出两个分身释放螺旋丸,属于远程技能,但释放时的施法动作容易被打断,点击是本体出去,......
  • Java设计模式之代理模式:静态代理VS动态代理,与其他模式的对比分析和案例解析
    一、代理模式简介代理模式(ProxyPattern)是一种结构型设计模式,它提供了一个代理对象,用来控制对另一个对象的访问。这种模式通常用于在访问对象时引入额外的功能,而不改变对象的接口。代理模式的核心思想是为其他对象提供一种代理,以控制对这个对象的访问。在现实生活中,代理模......
  • 设计模式之责任链模式
    责任链模式是面向对象的23种设计模式中的一种,属于行为模式范围。责任链模式(ChainofResponsibility),见名知意:就是每一个处理请求的处理器组合成一个链表,链表中的每个节点(执行器)都有机会处理发送的请求。大致的结构是这个样子: 举一个简单的例子:某公司有一名新员工要入职,则入职......
  • Flannel Wireguard 模式
    FlannelWireGuard模式一、环境信息主机IPubuntu172.16.94.141软件版本docker26.1.4helmv3.15.0-rc.2kind0.18.0clab0.54.2kubernetes1.23.4ubuntuosUbuntu20.04.6LTSkernel5.11.5内核升级文档二、安装服务kind配置......
  • Flannel IPsec 模式
    FlannelIPSec模式一、环境信息主机IPubuntu172.16.94.141软件版本docker26.1.4helmv3.15.0-rc.2kind0.18.0clab0.54.2kubernetes1.23.4ubuntuosUbuntu20.04.6LTSkernel5.11.5内核升级文档二、安装服务kind配置文件......
  • 分布式事务的Seata AT模式原理
    Seata官网地址:https://seata.apache.org/zh-cn/AT模式优点:无侵入式代码,只需要添加注解,底层采用Seata代理的数据源DataSourceProxy缺点:依赖于数据库,目前只适用于postgresql、oracle、mysql、polardb-x、sqlserver、达梦数据库等数据库,比如业务逻辑中含有redis、es等操作需要控......
  • 设计模式简介及PHP的35种设计模式(上)
    什么是模式??        有经验的00开发者(以及其他的软件开发者)建立了既有通用原则又有惯用方案的指令系统来指导他们编制软件。如果以结构化形式对这些问题、解决方案和命名进行描述使其系统化,那么这些原则和习惯用法就可以称为模式。例如,下面是一个模式样例:    ......
  • 深度学习设计模式之策略模式
    文章目录前言一、介绍二、特点三、详细介绍1.核心组成2.代码示例3.优缺点优点缺点4.使用场景总结前言策略模式定义一系列算法,封装每个算法,并使它们可以互换。一、介绍策略模式(StrategyPattern)是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使......