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

相关推荐
面朝大海,春不暖,花不开32 分钟前
Java服务提供者模式实现指南
java·开发语言·python
有你有我OK34 分钟前
java 验证ip是否可达
java·开发语言·tcp/ip
不知疲倦的仄仄38 分钟前
RabbitMQ多角度可靠性分析/基于Java代码深度解析
java·spring·rabbitmq·java-rabbitmq
真实的菜3 小时前
适配器模式:接口转换的神奇魔法[特殊字符],让不兼容的类和谐共处!
java·适配器模式
骚戴3 小时前
SpringBoot源码解析(十五):spring-boot-autoconfigure.jar的模块化设计
java
YuTaoShao3 小时前
Java八股文——计算机网络「应用层篇」
java·网络·计算机网络
Mryan20053 小时前
Android 应用多语言与系统语言偏好设置指南
android·java·国际化·android-studio·多语言
鲁Q同志4 小时前
若依导出模板时设置动态excel下拉框(表连接的)
java·excel
汇匠源5 小时前
Java 零工市场小程序 | 灵活就业平台 | 智能匹配 | 日结薪系统 | 用工一站式解决方案
java·小程序