【设计模式学习1】什么是单例模式?单例模式的几种实现。

一、什么是单例模式

单例模式是在内存中只创建一个对象的模式,它保证一个类只有一个实例。

二、单例模式的几种实现

(一)懒汉式单例模式

java 复制代码
/**
 * 懒汉式单例模式
 * (懒加载,需要的时候在去加载)
 * 优点:需要SingleObject时,才会去实例化,节省空间
 * 缺点:在多线程环境下,getSingleObject() 方法并不是线程安全的
 */

public class SingleObject {
    //私有化对象(未实例化)
    private static SingleObject object;

    //私有化构造方法
    private SingleObject() {};

    //获取私有化对象
    public static SingleObject getSingleObject() {
        //bean为null才去实例化,否则直接返回。
        if (object == null) {
            object = new SingleObject();
        }
        return object;
    }
}

(二)饿汉式单例模式

java 复制代码
/**
 * 饿汉式单例模式
 * (线程安全,但会造成资源浪费)
 * 优点:在类加载时候创建实例,不存在线程安全问题
 * 缺点:占用系统资源,如果这个实例没有被用到,则会造成资源浪费
 */
public class Single {
    //私有化对象(已实例化)
    private static Single object = new Single();

    //私有化构造方法
    private Single() {};

    //获取私有化对象
    public static Single getSingle() {
        return object;
    }

}

(三)双重校验加锁式单例模式(线程安全)

java 复制代码
public class SingleObject{
    //volatile关键字防止指令重排序造成的空指针异常(通过插入特定的内存屏障的方式来禁止指令重排序)
    private static volatile SingleObject object;

    //私有构造方法
    private SingleObject() {}

    public static SingleObject getSingleObject() {
        //第一次检查防止每次获取bean都加锁
        if (object == null) {
            //加锁,防止第一次创建实例化时,并发线程多次创建对象
            synchronized (SingleObject.class) {
                //第二次检查判断对象没有实例化,则进行对象的实例化
                if (object == null) {
                    object = new SingleObject();
                }
            }
        }
        return object;
    }
}

三、问题分析

(一)懒汉模式为什么会有线程安全问题?

如上图所示,多线程情况下,在时刻T,线程A和线程B都判断single为null,从而进入if代码块中都执行了new Single()的操作创建了两个对象,就和我们当初的单例初衷相悖而行。

(二)为什么要进行两次判空,他们的作用分别是什么?加锁的作用是什么?

1、第一次判空目的:为了缩小锁的粒度,避免每次获取实例都需要进行加锁。

2、加锁目的:在首次获取对象实例时,防止并发线程多次创建对象。

3、第二次判空目的:如果对象没有进行实例化,则进行对象实例化操作

(三)volatile关键字修饰对象的作用是什么?

volatile关键字防止指令重排序 造成的空指针异常(通过插入特定的内存屏障的方式来禁止指令重排序)

指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能。

创建一个对象,在JVM中会经过三步:

  • (1)为object对象分配内存空间
  • (2)初始化object对象
  • (3)将object指向分配好的内存空间

由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getSingleObject() 后发现 object不为空,因此返回 object,但此时 object还未被初始化,就会出现空指针异常。

相关推荐
Advancer-14 分钟前
第二次蓝桥杯总结(上)
java·算法·职场和发展·蓝桥杯
\xin25 分钟前
pikachu自编SQL(POST)
java·数据库·sql
一行代码一行诗++27 分钟前
注释是什么和注释该怎么写(C语言)
java·前端·javascript
JAVA面经实录9171 小时前
企业级java+LangChain4j-RAG系统 限流熔断降级
java·开发语言·分布式·langchain
Drug1 小时前
Struts2 从入门到放弃?不,这些核心知识你依然需要掌握
java
Slow菜鸟1 小时前
Codex CLI 教程(五)| AI 驱动项目从零到一:面向 Java 全栈工程师打造个人 ECC(V2版)
java·开发语言·人工智能
月落归舟1 小时前
java基础之拷贝、单例
java·单例·拷贝
鬼蛟1 小时前
什么是 Git
java
直奔標竿2 小时前
SpringAI + RAG + MCP + Agent 零基础全栈实战(完结篇)| 27课完整汇总,Java开发者AI转型必看
java·开发语言·人工智能·spring boot·后端·spring
云烟成雨TD2 小时前
Spring AI 1.x 系列【31】向量数据库:进阶使用指南
java·人工智能·spring