说说单例模式

今天来说说单例模式(Singleton),这是一种在日常开发中比较常用的软件设计模式,在使用这个设计模式时,单例对象的类必须保证只有一个实例对象存在。

使用单例模式的好处:

  1. 能够避免实例对象的重复创建,可以减少每次创建对象的时间开销,还能节约内存空间。
  2. 可以避免由于操作多个实例导致的逻辑错误。

下面介绍一下单例模式的几种写法。

1. 饿汉式

csharp 复制代码
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

从上面代码可以看到,使用单例模式的类的构造器要使用private修饰符,这样可以保证在其他类里面不能实例化该单例类。从private static Singleton instance = new Singleton();可以看出,饿汉式是在类加载的时候就对实例进行创建(在类加载阶段会执行static静态变量的初始化)。

优点:因为是在类加载的时候创建的实例,不会存在多条线程创建多个实例的情况。

缺点:即使这个单例对象没有被使用到也会被创建出来,浪费了内存。

2.懒汉式

csharp 复制代码
public class Singleton {
    private static Singleton instance= null;// 不实例化
    private Singleton(){}
    public static Singleton getInstance(){
        // 当 instance 为 null 时,则实例化对象,否则直接返回对象
        if(null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

优点:这种写法的好处就是在你需要的时候才去创建单例对象,这样就不会浪费内存了,如果单例对象已经创建,再次调用获取方法将不会去创建新的对象,而是返回之前创建的对象。

缺点:我们这里的懒汉式写法没有考虑线程安全的问题,如果有多条线程并发调用getInstance方法,可能会导致创建出多个实例对象,因此我们需要加上锁来解决线程安全问题:

csharp 复制代码
public class Singleton {
    private static Singleton instance= null;// 不实例化
    private Singleton(){}
    //加上同步锁
    public static synchronized Singleton getInstance(){
        // 当 instance 为 null 时,则实例化对象,否则直接返回对象
        if(null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

3.双重检查锁模式

csharp 复制代码
public class Singleton {
    private static Singleton instance= null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(null == instance){// 第一次检查,当 instance 为 null 时,则实例化对象,否则直接返回对象
            synchronized (Singleton.class){// 同步锁
                if(null == instance){// 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

加了锁的懒汉式既解决了线程并发问题,又实现了延迟加载,看似很好,但是它是存在着性能问题的,synchronized修饰的同步方法要比一般方法慢很多,如果多次调用获取实例方法,累积的性能损耗就大了,所以才有了上面的双重检查锁的写法。

所谓的双重检查,不就是检查两次是否已经有这个单例对象了嘛,因为单例对象只需要创建一次,后面再次调用getInstance方法只需直接返回单例对象,因此大部分情况下,调用getInstance方法都不会执行到同步代码块,相比于直接把synchronized加在方法级别上,这样就提高了程序性能。

4.静态内部类

csharp 复制代码
public class Singleton {
    private Singleton(){}
    public static class InnerSingleton{
        private static Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return InnerSingleton.instance;
    }
}

这种写法也是利用了类加载机制来保证只创建一个实例,因此也不会存在多线程安全的问题,只不过这里是在内部类里面去创建对象实例,这样写的话只要应用中不使用内部类,JVM就不会去加载这个内部类,也就不会去创建单例对象,这样就实现了懒汉式的延迟加载,也就是说这种写法可以同时保证延迟加载和线程安全。

总结

  1. 饿汉式在类加载时就实例化单例对象,因此不存在线程安全问题。
  2. 懒汉式默认不实例化,只在首次调用时才创建单例对象。但懒汉式在多线程环境下可能存在线程安全问题,因此我们采用双重检查的方式来确保其线程安全。
相关推荐
rannn_11121 分钟前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
qq_124987075325 分钟前
基于JavaWeb的大学生房屋租赁系统(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·计算机视觉·毕业设计·计算机毕业设计
短剑重铸之日31 分钟前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
若鱼19191 小时前
SpringBoot4.0新特性-Observability让生产环境更易于观测
java·spring
觉醒大王1 小时前
强女思维:着急,是贪欲外显的相。
java·论文阅读·笔记·深度学习·学习·自然语言处理·学习方法
努力学编程呀(๑•ี_เ•ี๑)1 小时前
【在 IntelliJ IDEA 中切换项目 JDK 版本】
java·开发语言·intellij-idea
码农小卡拉1 小时前
深入解析Spring Boot文件加载顺序与加载方式
java·数据库·spring boot
向上的车轮1 小时前
为什么.NET(C#)转 Java 开发时常常在“吐槽”Java:checked exception
java·c#·.net
Dragon Wu1 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
跳动的梦想家h2 小时前
环境配置 + AI 提效双管齐下
java·vue.js·spring