【设计模式学习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还未被初始化,就会出现空指针异常。

相关推荐
diving deep3 小时前
springboot集成日志配置文件
java·spring boot·后端·logback
蟹至之4 小时前
【Java】异常的初步认识
java·开发语言·类和对象·异常
广西千灵通网络科技有限公司4 小时前
基于Java的话剧购票小程序【附源码】
java·小程序·apache
苏小瀚4 小时前
[Java] idea的调试介绍
java·intellij-idea
JWenzz14 小时前
Redis删除策略
java·数据库·redis·缓存
幻听嵩的留香4 小时前
javaEE课程项目-壁纸管理系统
java·java-ee
liubo666_4 小时前
SpringMVC(结合源码浅析工作流程)
java·spring·springmvc
speop5 小时前
TASK05【Datawhale 组队学习】系统评估与优化
android·java·学习
星沁城5 小时前
108. 将有序数组转换为二叉搜索树
java·数据结构·leetcode
在未来等你5 小时前
互联网大厂Java求职面试:云原生架构与AI应用集成解决方案
java·spring cloud·微服务·ai·云原生·kubernetes·大模型