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

设计模式-单例模式

时间:2023-05-11 17:24:33浏览次数:34  
标签:Singleton singleton 模式 实例 线程 单例 static 设计模式

单例模式

概述

Java 单例模式是一种创建对象的设计模式。单例模式确保一个类只有一个实例被创建,并提供一个全局访问点来访问该实例。这种模式是一种创建型模式,它涉及一个单一的类,该类负责创建自己的一个对象,同时确保只能创建一个对象。

实现方式

饿汉式
public class Singleton {
    /**
     * 私有化构造器,外部不能进行实例化
    **/
    private Singleton() {}

    /**
     * 类加载时就进行实例化
     * private 外部不能进行访问&修改
     * final 保证属性不会被修改,final特征的变量在编译时就绑定了值,而不是在运行时,提高了程序的性能
     * 静态变量在内存中只有一个拷贝,JVM只为静态变量分配一次内存,保证了线程安全
     */
    private final static Singleton SINGLETON = new Singleton();

    /**
     * 提供一个公有的方法,返回实例对象
     */
    public static Singleton getInstance() {
        return SINGLETON;
    }
}

优点:

  1. 线程安全:在类加载时就进行实例化,只有一个实例对象存在,因此不存在线程安全性问题;
  2. 实现简单:实现简单,代码实现也比较简单;
  3. 没有锁机制,执行效率高。

缺点:

  1. 没有懒加载:由于是在类加载时就进行实例化,所以可能会导致内存浪费,无法满足懒加载的需求;
  2. 无法通过继承来进行扩展:由于构造器是私有的,不能通过继承来生成子类的实例;
  3. 可读性差:在修饰static变量时需要同时使用final和static修饰,并且是在变量声明时进行初始化,这会降低代码的可读性。

饿汉式单例模式适用于只需要一个实例对象,而且实例对象在程序运行期间不需要更改的情况下,是一种简单高效的实现方式。但是如果需要更高的灵活性和扩展性,建议使用懒汉式单例模式等其他单例模式来实现。

懒汉式
  • 线程不安全
public class Singleton {
    /**
     * 私有化构造器,外部不能进行实例化
    **/
    private Singleton() {}

    /**
     * 声明一个静态的实例
     **/
    private static Singleton singleton;

    /**
     * 提供一个全局的静态方法
     * 用到的时候才去实例化
     * 在多线程的情况下,可能会出现多个实例
     */
    public static Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }

        return singleton;
    }
}
  1. 线程安全
/**
 * @author yiwenjie
 * 懒汉式单例
 */
public class Singleton {
    /**
     * 私有化构造器,外部不能进行实例化
    **/
    private Singleton() {}

    /**
     * 声明一个静态的实例
     **/
    private static Singleton singleton;

    /**
     * 提供一个全局的静态方法
     * 用到的时候才去实例化
     * synchronized 保证线程安全
     */
    public static synchronized Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }

        return singleton;
    }
}
  • 优点:
  1. 节省了系统资源。只有当需要使用单例对象时才进行实例化,避免了一开始就占用内存资源的问题。

  2. 具有延迟加载的特性,即按需加载,提高了程序的性能和响应速度。

  • 缺点:
  1. 在多线程的情况下,如果没有进行同步处理,可能会出现多个线程同时判断singleton为null的情况,从而导致实例化多个对象的问题。
  2. 存在并发风险。需要特别注意,如果实例化单例的代码存在并发风险时,如多个线程同时执行getInstance()方法,可能会导致多个实例被创建。
  3. synchronized关键字的影响。如果使用synchronized关键字进行同步处理,会影响getInstance()方法的性能,降低程序的执行效率
双重检查
/**
 * @author yiwenjie
 * 懒汉式单例
 */
public class Singleton {
    /**
     * 私有化构造器,外部不能进行实例化
    **/
    private Singleton() {}

    /**
     * 声明一个静态的实例
     * volatile关键字保证了变量的可见性,防止指令重排序
     **/
    private static volatile Singleton singleton;

    /**
     * 提供一个全局的访问点
     * 先判断是否为空,为空则加锁,再判断是否为空,为空则创建实例
     */
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                if (null == singleton) {
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}

优点:

  1. 线程安全:通过双重校验锁,保证了线程安全。
  2. 懒加载:只有在第一次调用getInstance方法时才会创建对象,节省了内存。
  3. 高效性:相对于单重校验锁的方式,使用双重校验锁的方式,在保证线程安全的情况下,可以提高效率,在多线程环境下性能得到大大提升。

缺点:

  1. 可能会引发空指针异常:如果在某个线程创建对象时,其他线程未执行第一个if判断而直接返回了singleton的值,此时将会引发NullPointerException异常。
  2. 可读性差:由于双重校验锁的代码比较复杂,可读性比较差,可读性是一种代码的质量,可读性差的代码维护难度大。

总之,双重校验锁单例模式在多线程高并发环境下比较常用,但需要注意线程安全和空指针异常问题。在单线程或者低并发环境下,可以使用饿汉式单例模式,代码简单,可读性好。

静态内部类
public class Singleton {
    /**
     * 私有化构造器,外部不能进行实例化
    **/
    private Singleton() {}

    public static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

优点:

  1. 线程安全:由于JVM在加载静态内部类的时候进行了同步,所以保证了线程安全。
  2. 懒加载:只有在第一次调用getInstance方法时才会创建对象,节省了内存。
  3. 高效性:相对于双重校验锁的方式,使用静态内部类的方式,在保证线程安全的情况下,可以提高效率,在多线程环境下性能得到大大提升。
  4. 线程安全和懒加载的最佳实践:该方式是线程安全和懒加载的最佳实践,代码简单易懂,可读性好。

缺点:

  1. 可能会引发反序列化漏洞:如果该类被反序列化到内存中,反序列化时会重新创建新的实例,破坏单例模式。
  2. 不支持传参:该实现方式没有办法支持传参。 总的来说,静态内部类实现单例模式是一种优秀的选择,具有懒加载和线程安全的特性,可以保证单例模式的正确性和可读性。但是需要注意对象可能会被序列化,因此可以考虑增加readResolve方法防止反序列化攻击。
枚举
public enum Singleton {
    SINGLETON;

    public void add() {
        System.out.println("add...");
    }
}

注意事项

当想实例化一个单例类的时候,必须要记住使用相应的获得对象的方法,而不是使用new

使用场景

需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

标签:Singleton,singleton,模式,实例,线程,单例,static,设计模式
From: https://www.cnblogs.com/ywjcqq/p/17391673.html

相关文章

  • java代理模式及动态代理类
    1.     代理模式代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式一般涉及到的角色有:抽象角色:声明真实对象和代理对象的共同接口;代理角色:......
  • 直播网站程序源码,【openpyxl】只读模式、只写模式
    直播网站程序源码,【openpyxl】只读模式、只写模式1.只读模式只读模式,如果你需要读取很大的Excel文件,但是又不改变和保存,例如只读取数值用于其他数据分析,这时候我们完全可以使用只读模式提供性能 fromopenpyxlimportload_workbook#加载Excel文件时使用read_only指定只读模......
  • 原型模式(Prototype Pattern)
    模式动机原型模式(PrototypePattern)结构较为简单,它是一种特殊的创建型模式,当需要创建大量相同或者相似对象时,可以通过对一个已有对象的复制获取更多对象。Java语言提供了较为简单的原型模式解决方案,只需要创建一个原型对象,然后通过在类中定义的克隆方法复制自己。该模式应用较......
  • SqlSugar-C#版(二)_SqlSugar单例
    /***┌──────────────────────────────────────────────────────────────┐*│描述:SqlSugar数据库连接类-单例*│作者:执笔小白......
  • 操作系统常见的三种调度模式
    1、高级调度,也叫作业调度,决定把外存上处于后备队列中的哪些作业调入内存,并为它们创建进程、分配必要的资源,排入就绪队列。数据结构有后备队列,数据元素为JCB(作业控制块)。2、中级调度,也叫作交换调度,为提高内存利用率和缓解内存紧张而引入。决定把哪些进程挂起并从内存交换到外存,又......
  • 责任链设计模式进行多级审批
    业务场景:在上篇泛型用法中将所有的审批业务都通过泛型方法进行插入待审核记录了,现在便需要对这些待审记录进行审批,由于客户需要多级审批,也就是每个业务的审批人数都不相同为此引入责任链设计模式1.首先,创建五级审核人表(最多五级),对各个业务类型,部门分配对应的审核人 2.创建责任......
  • 建造者模式(Builder Pattern)
    模式动机建造者模式(BuilderPattern)是最复杂的创建型模式,它用于创建一个包含多个组成部分的复杂对象,可以返回一个完整的产品对象给用户。它通过将客户端与包含多个组成部分的复杂对象的创建过程分离,使得客户端无需知道复杂对象的内部组成部分与装配方式,只需要知道建造者的类型......
  • Qt开发-共享内存使用范例,配合开发者密钥使用后台调试程序或者进入调试模式
    共享内存就之前不是开发了一个Leventure_DeveloperKey用以调试程序嘛,在这里简单聊一下调试模式的方案。这里的调试分为了两种,一种是调试模式,一种是开发者模式。需要这两种模式的原因也很简单:1.在远程调试的时候,我可能需要程序从头开始进入调试,这就要求程序一直卡在开头的某个位......
  • IBM Power 740 7 单用户引导模式
     2、单用户引导模式在AIX上要进入单用户引导模式,只需把钥匙拨到维护(SERVICE)位置,然后系统上电即可。系统首先显示的是诊断操作指令(DiagnosticOperatingInstructions)的屏幕。在这幅屏幕按回车进入功能选择(FunctionSelection)菜单。类似如下图:FUNCTIO......
  • C#设计模式18——迭代器模式的写法
    是什么:迭代器模式是一种行为型设计模式,它允许客户端通过一种统一的方式遍历集合对象中的元素,而无需暴露集合对象的内部结构。为什么:使用迭代器模式可以使得客户端程序与集合对象解耦,从而可以更加灵活地对集合对象进行遍历操作。此外,迭代器模式还可以提高代码的复用性,简化客户端......