设计模式之单例模式

简介

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

饿汉模式:

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

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

相关推荐
哈哈哈哈~3 小时前
Java中的单例模式
java·单例模式
纪莫3 小时前
技术面:Spring(循环依赖,spring与springboot的区别)
java·spring·java面试⑧股
oak隔壁找我3 小时前
Spring Boot MongoDB 使用技巧
java·后端
嫄码4 小时前
BigDecimal对象比较时的注意事项
java
我是华为OD~HR~栗栗呀4 小时前
华为OD-23届考研-测试面经
java·c++·python·华为od·华为·面试·单元测试
敲代码的嘎仔4 小时前
JavaWeb零基础学习Day4——Maven
java·开发语言·学习·算法·maven·javaweb·学习方法
残花月伴4 小时前
Consumer 和 Function 接口详解
java
W.Buffer4 小时前
设计模式-工厂模式:解耦对象创建的设计艺术
java·开发语言·设计模式
WaWaJie_Ngen4 小时前
【设计模式】建造者模式(Builder)
设计模式·建造者模式