Java中的单例模式

目录

[1 什么是单例模式](#1 什么是单例模式)

[2 单例模式的分类](#2 单例模式的分类)

[2.1 饿汉模式](#2.1 饿汉模式)

[2.2 懒汉模式](#2.2 懒汉模式)


1 什么是单例模式

单例模式是Java中的一种设计模式,它强制要求某一个类,在某一个程序中,必须只有一个实例。这个类提供了一种访问方式,可以让其他类进行直接访问它的对象,而不需要实例化该类的对象。

单例模式的目的是解决频繁创建和销毁全局使用的类实例的问题,使用单例模式能够减少开销。

注意:

  1. 单例类只能有一个实例
  2. 单例类必须自己创建自己唯一的实例
  3. 单例类必须给所有其他对象提供这一实例

2 单例模式的分类

单例模式根据创建实例的时机不同分为饿汉模式和懒汉模式:

2.1 饿汉模式

饿汉模式,听这个名字就感觉很迫不及待。所以饿汉模式是在类加载的同时就创建实例。

下面使用一个简单的例子进行演示:

java 复制代码
class SingletonHungry{
    // 只创建一个实例
    // 使用 static 修饰,instance是一个类对象,且有且只有一个
    private static SingletonHungry instance = new SingletonHungry();
    // 私有的构造方法 保证在该类外部无法进行实例化对象
    // 可以有多个构造方法 但是必须都是使用 private 修饰的
    private SingletonHungry(){

    }
    private SingletonHungry(int n){

    }
    // 给外部的类提供一个能获取到 SingletonHungry 类实例的方法 必须是用 public 修饰的
    public static SingletonHungry getInstance(){
        return instance;
    }
}
public class Demo {
    public static void main(String[] args) {
        // 无法直接进行 new 对象,因为构造方法是私有的
//        SingletonHungry instance = new SingletonHungry();

        // 使用类名.的方式来使用 getInstance() 方法 (因为是用 static 修饰的方法)
        SingletonHungry instance1 = SingletonHungry.getInstance();
        SingletonHungry instance2 = SingletonHungry.getInstance();

        // 打印出来的结果为 true
        // 意味着这两个对象事实上指向的是同一个对象
        // 其实 结果为true是必然的 因为在Singleton类中,我们就只创建了一个实例
        System.out.println(instance1 == instance2);
    }
}

2.2 懒汉模式

懒汉模式,与饿汉模式不同的是,懒汉模式是在需要的时候再进行创建实例。

相同地,下面继续用一个简单的例子进行演示:

java 复制代码
class SingletonLazy{
    // 先声明一个 instance 但是并没有 new 一个实例 而是让它先为 null
    // 在后面需要用的时候 在进行 new 一个具体的实例
    // 这里使用 volatile 关键字,是为了在多个线程同时访问这个类的时候,保证线程安全,保证这个类只有一个实例
    private static volatile SingletonLazy instance = null;

    // 构造方法和饿汉模式一样,都是私有的
    private SingletonLazy(){

    }

    // 在需要使用实例的时候再进行 new 一个实例
    public static SingletonLazy getInstance(){
        // 外层的 if 判断是否存在实例,如果不存在实例,那么加锁并且创建一个实例
        // 内存的 if 再次判断是否存在实例
        // 是因为在多线程中首次调用 getInstance 所有线程都经过了外层的 if 发现了并没有实例
        // 接下来竞争锁,其中一个线程竞争到了锁,并且创建了一个实例
        // 当这个实例创建完成之后,instance 就并不为空
        // 那么接下来竞争到锁的线程就会因为内层的 if 判断 已经存在实例,就不会再创建新的实例了
        // 等到之后再调用 getInstance 方法的时候,就已经存在实例了,直接 return就好了
        if(instance == null){
            // 懒汉模式的线程不安全存在于第一次创建实例的时候
            // 如果是给getInstance方法整体上锁,那么每一次调用这个方法就需要进行加锁解锁操作
            // 所以 在这里上锁是为了减少加锁解锁的开销
            synchronized (SingletonLazy.class){
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

}
public class Demo {
    public static void main(String[] args) {
        // 无法直接进行 new 对象,因为构造方法是私有的
//        SingletonLazy instance = new SingletonLazy();
        // 使用类名.的方式来使用 getInstance() 方法  (因为是用 static 修饰的方法)
        SingletonLazy instance1 = SingletonLazy.getInstance();
        SingletonLazy instance2 = SingletonLazy.getInstance();

        // 打印出来的结果为 true
        // 意味着这两个对象事实上指向的是同一个对象
        // 其实 结果为true是必然的 因为在SingletonLazy类中,我们就只创建了一个实例
        System.out.println(instance1 == instance2);
    }
}

懒汉模式和饿汉模式最大的区别就在于,懒汉模式需要考虑到线程安全的问题,但是饿汉模式因为是在类加载的时候就创建实例,所以不需要考虑线程安全问题。

相关推荐
要开心吖ZSH3 分钟前
定时任务是用外部调度(xxl-job)还是本地化( @Scheduled)更香?以号源刷新任务为例?
java·xxl-job·定时任务·scheduled
nagualky1235 分钟前
GPT-4 系列退役在即:一套可回滚的模型迁移验收法
java·开发语言·ai·架构
今天的砖头有点烫手啊5 分钟前
Java 8 Stream 原理拆解:惰性求值与并行流陷阱
java·jvm
starzy199013 分钟前
Flink 任务调度 Graph 源码深度剖析:从 StreamGraph 到 ExecutionGraph 的三层演进
java·大数据·flink
Eyfcom14 分钟前
〖实战〗快运系统 Oracle 性能优化:从“能用”到“扛住双11”的 Java + Spring Boot 实战
java·oracle·性能优化·快递管理系统
m0_5873830019 分钟前
碰碰好评卡功能落地全流程实战指南 附核心逻辑与常见问题解决方案
java·前端·数据库·后端·struts·需求分析
cheems952720 分钟前
从统一响应到 Swagger:一篇讲清微服务接口文档设计未命名
java·微服务·架构·状态模式
Y38153266223 分钟前
SerpBase + RAG 实战:用搜索 API 给知识库接上实时数据,回答不再过期
java·服务器·数据库
开开心心就好32 分钟前
文件关联被破坏怎么修?免费工具一键修复
java·服务器·开发语言·pdf·ocr·启发式算法·宽度优先
不停喝水33 分钟前
【前端转全栈java速通课】 项目实战④12-14节 拦截器过滤器和全局配置-cors跨域设置-token体系-利用session做验证码
java·开发语言·前端·ai·容器