设计模式之单例模式

简介

单例模式是一种设计模式,核心思想是确保一个类只有一个实例,私有化类的构造方法(防止外部直接创建)并提供一个全局访问点,一个实例进行复用可以避免频繁的创建和销毁对象,是非常简单且在面试时高频出现的设计模式,单例模式分为饿汉模式和懒汉模式,本篇就来盘一盘

饿汉模式:

顾名思义,是指该对象的实例非常迫切的被实例化,具体有多早呢,使用static关键字修饰,使得该实例在类加载时就一起被创建,获取实例很快并且不会有线程安全问题,不过是正是由于在类加载时实例化,如果对象的数据较多,则会影响类加载的时间。

懒汉模式:

是指不着急实例化对象,在首次需要的时候才会被实例化,不过这种最简易的懒汉单例模式在多线程下会有线程安全问题,例如多个线程同时访问getDemo()方法,都会认为还没有创建实例,从而创建出多个实例,这就不是单例模式了

线程安全的懒汉模式:

直接上完全体版本,以多线程并发视角打开这张图,首次检查,多个线程同时判断未创建实例,进入到synchronized修饰的代码快,此时只有一个线程能够进入,其他线程都会被阻塞,当唯一进入的线程创建好实例后执行结束释放锁时,其他被阻塞的线程依次获取到锁进入,再次判断实例是否被创建,前人栽树后人乘凉,此时已经有实例了,就会直接返回实例 ,不过不排除有那么一点点点概率发生前面的线程创建实例但内存刚好没有刷新,从而使后面的线程判断到还是没有创建实例,导致变成多例,于是在用于存储实例的变量上加上volatile关键字,保证线程对该变量的修改及时刷新到主内存中,volatile关键字详见>>>>>volatile关键字的作用(拿着demo自己试试)

java 复制代码
public class Demo {
    //静态私有成员变量,用于存储唯一的实例,使用volatile修饰保证其他线程的更新能被刷新到主内存
    private static volatile Demo instance ;

    //私有化构造方法,防止外部直接创建
    private Demo() {

    }
    //全局唯一访问点
    public static Demo getDemo() {
        // 第一次检查:如果实例尚未创建则创建
        if (null == instance) {
            //加锁,保证同时只有一个线程进入创建实例,可能会有多个线程同时判断到没有创建实例,从而都被阻塞在这里
            synchronized (Demo.class) {
                //再次校验,以免多个线程在锁释放时直接创建实例,这便是双重检验
                if (null == instance) {
                    instance = new Demo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        Demo demoOne = Demo.getDemo();
        Demo demoTwo = Demo.getDemo();

        System.out.println(demoOne.equals(demoTwo));
    }
}

其他实现方式:

1.枚举

枚举就是单例模式,实现非常简洁,其构造方法默认就是私有的,并且枚举的实例也是在类加载时就已经创建,我们可以通过以下例子来玩一玩

2.静态内部类
这个厉害了,实现简单,支持懒加载并且线程安全(还有高手)

类加载的线程安全机制

JVM在类加载过程中,特别是在初始化阶段,会自动获取一个锁,确保同一个类在多线程环境下只会被加载和初始化一次
静态内部类的延迟加载特性:

在静态内部类实现中,单例实例是通过静态内部类的静态成员持有的,外部类被加载时,静态内部类并不会立即被加载和初始化,只有在首次调用getInstance()方法时,静态内部类才会被加载

总结:

1.单例模式就是确保一个类只有一个实例,私有化类的构造方法并提供一个全局访问点,避免对象频繁创建和销毁

2.分为饿汉模式和懒汉模式,常规懒汉模式有线程安全问题,需要双重检验加锁

3.枚举也是饿汉单例模式,静态内部类实现单例模式既可以懒加载又是线程安全的


上一篇 >>>>> volatile关键字的作用(拿着demo自己试试)

看千遍想万遍,还得动手练一练,量变才能产生质变

相关推荐
weixin_440784112 小时前
【HandlerThread实现原理】
android·java·开发语言
leoZ2312 小时前
本地跑大模型实战(七):llama.cpp 性能调优,让推理更快更省
java·人工智能·spring·生成对抗网络·语言模型·自然语言处理·llama
mubei-1234 小时前
SpringDAO的用法
java·开发语言·数据库
happymagic4 小时前
java spring boot做的jar包程序,如何实现自动运行启动
java·运维·服务器·spring boot·jar
都叫我大帅哥6 小时前
给 Java 程序员的 CSRF 攻击详解:从原理到防御实战
java
江南十四行6 小时前
Spring框架核心(上)——IoC控制反转与DI依赖注入详解
java·后端·spring
fb_123457 小时前
Linux磁盘分区从入门到实操:MBR_GPT全解析+分区工具实战指南
java·linux·gpt
董员外8 小时前
RAG 系统进化论(八):Agentic RAG(智能体式 RAG),从回答问题到完成任务
人工智能·后端·设计模式
码智社8 小时前
JSON 零基础到资深全体系教程
java·json
wgego9 小时前
基础的反序列化一些总结(php和java)
java·开发语言·笔记