[JavaEE]单例模式(以懒汉模式和饿汉模式为例)

Author:MTingle

major:人工智能


Build your hopes like a tower!


一、单例模式是什么?

单例模式是设计模式的一种,单例 = 单个实例,在一个Java进程中,要求特定的类,只能有唯一一个实例,通过特殊的技巧,来确保这里的实例不会有很多个(尝试 new 多个实例的时候,编译器会直接报错)

二.为什么需要有单例模式呢?

举个栗子,有的时候,代码中需要有一个对象来管理,持有大量数据,此时有一个对象就可以了,比如,一个对象管理了 10G 的数据,如果你不小心创建出了多个对象,内存空间机会成倍增长,机器就顶不住了.

那么如何确保对象是唯一的呢?

期望让机器(让编译器)能够对代码中的指定的类,创建的实例个数进行校验,如果发现创建多个实例了,就直接让编译器报错,此时就可以避免因失误创建出多个实例了.

在Java可以通过一些"奇技淫巧"来实现这个效果,此处介绍两种最基础的实现方式:懒汉模式与饿汉模式.

三、饿汉模式

java 复制代码
class Singleton{
    private static Singleton instance=new Singleton();

    public static Singleton getInstance(){
        return instance;
    }
    private Singleton(){

    }

}

public class ThreadDemo26 {
    public static void main(String[] args) {
        // Singleton singleton1=new Singleton(); //报错
        Singleton singleton=Singleton.getInstance();
    }
}
复制代码
private static Singleton instance=new Singleton();

static是静态的,指的是"类属性", instance 是 Singleton 类对象里面持有的属性, Singleton.class 从 .class 文件加载到内存中,表示这个类的一个数据结构,因此,instance指向的这个对象就是唯一一个对象

复制代码
public static Singleton getInstance(){return instance;}

其他代码如果想要使用这个类的实例,就需要通过这个方法来进行获取,不应该在其它代码中重新 new 这个对象,而是通过这个方法获取到现成的对象

复制代码
private Singleton(){ }

通过这个代码,其他代码就无法new了,从根本上不让其它人new出新的对象.只能使用getInstance获取.

上述代码就是"饿汉模式"的一种简单写法,而所谓的饿表示"非常急迫",实例是在类加载的时候就已经创建了,而创建的时机非常早,相当于程序一启动,实例就创建了,就使用"饿汉"形容"创建实例非常急迫 非常早".

并且 饿汉模式是线程安全的 ! ! !

四.懒汉模式

java 复制代码
class SingletonLazy {

    private volatile static SingletonLazy instance=null;  // volatile: 防止指令重排序
    public static Object locker=new Object();
    public static SingletonLazy getInstance() {
        if (instance==null) {  // 如果instance==null,说明是首次调用,首次调用就需要考虑线程安全问题,就要加锁
                              //如果非null,就说明是后续调用,就不需要加锁了
            synchronized (locker) {
                if (instance == null) { //此处的if判断的是是否创建对象----双重校验锁
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

}



public class ThreaDemo27 {
    public static void main(String[] args) {

    }
}

懒汉模式 创建实例的时机不太一样,创建实例的时间只会更晚,知道第一次使用的时候,才会创建实例,顾名思义为"懒汉模式".

如果是首次调用 getInstance ,那么此时 instance 引用为 null ,就会进入 if 条件,从而把实例创建出来.如果是后续再次调用 getInstance ,由于 instance 已经不再是 null ,此时不会进入 if ,直接返回之前创建好的引用了.

这样的设定仍然可以保证,该类的实例是唯一一个,与此同时,创建实例的时机就不是程序驱动时了

懒汉模式是线程不安全的 ! ! !

因此,我们对他嵌套了多个 if 语句,具体功能如下

复制代码
if (instance==null) {  // 如果instance==null,说明是首次调用,首次调用就需要考虑线程安全问题,就要加锁
                      //如果非null,就说明是后续调用,就不需要加锁了
    synchronized (locker) {
        if (instance == null) { //此处的if判断的是是否创建对象----双重校验锁
            instance = new SingletonLazy();
        }
    }
}

通过这两次加锁,我们还是不能完全保证线程是安全的,在代码中,我们还使用了关键字volatile ,这是为了防止指令重排序

JVM在执行new创建对象的时候会执行下面三条指令

1.分配内存空间

2.初始化对象

3.使变量指向对象

由于JVM进行指令重排,以上指令顺序可能发生变化,可能变成如下顺序

1.分配内存空间

2.使变量指向对象

3.初始化对象

假设现在有两个进程t1和t2

t1进程分配内存空间,使变量指向对象

t2进程此时调用该对象,会认为此时对象已经被初始化,但由于jvm的指令重排序,该对象并没有被初始化,此时就出现了线程不安全的状况!

注意了以上几点,我们创建出的懒汉对象就是线程安全的对象!


相关推荐
马剑威(威哥爱编程)7 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
gjh12087 小时前
单例模式和适配器模式的简单介绍
单例模式·适配器模式
无尽的大道1 天前
单例模式详解:如何优雅地实现线程安全的单例
单例模式
Hello.Reader2 天前
单例模式全面解析
单例模式
编程修仙2 天前
java的单例设计模式
java·单例模式·设计模式
L_cl3 天前
Python学习从0到1 day27 Python 高阶技巧 ③ 设计模式 — 单例模式
学习·单例模式·设计模式
ktkiko114 天前
Java中的设计模式——单例模式、代理模式、适配器模式
java·单例模式·设计模式
傻傻虎虎5 天前
【真题笔记】21年系统架构设计师案例理论点总结
单例模式·系统架构·uml·命令模式
Mr. zhihao5 天前
享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
单例模式·享元模式
南城花随雪。6 天前
Spring框架之单例模式 (Singleton Pattern)
java·spring·单例模式