设计模式之单例模式

简介

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

饿汉模式:

顾名思义,是指该对象的实例非常迫切的被实例化,具体有多早呢,使用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自己试试)

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

相关推荐
FQNmxDG4S7 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全8 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje9 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv79 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫9 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287929 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本9 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
yaoxin5211239 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
灰子学技术11 小时前
Envoy 使用的设计模式技术文档
设计模式
极客先躯11 小时前
高级java每日一道面试题-2025年11月24日-容器与虚拟化题[Dockerj]-runc 的作用是什么?
java·oci 的命令行工具·最小可用·无守护进程·完全标准·创建容器的核心流程·runc 核心职责思维导图