多线程(2)-设计模式:单列模式

Java设计模式有很多种,这些设计模式是为了约束代码,保证代码的正确性,本次介绍单例模式,是Java开发中比较典型的开发模式。

1.单例模式:单例模式意味着单个实例(对象),即在实现某个类的同一个进程中,只能出现一个实例(对象);实例(Instance)也是一个很广泛的词:一个对象,一个进程,一个服务器...

2.实现一个单例模式的类:

2.1:

static:修饰方法和属性,被此关键词修饰之后是类属性 / 类方法 ,不带static修饰的方法和属性称为 " 实例方法 / 实列属性";

这里在借助static,把类属性创建出来了,由于instance的属性是static,这里的初始化(new)操作,只会在该类(Singleton这个类)被加载时初始化一次;

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

2.2:饿汉模式的单例模式:

通过这三个步骤就可以创建出一个单例模式的类

java 复制代码
class Singleton{
    //通过static的作用,把这个类实例创建出来
    private static Singleton instance =new Singleton();
    //如果想使用这个实例,需要用方法获取他
    public static Singleton getInstance(){
        return instance;
    }
    //还需要防止程序员通过其他途径创建Singleton的实例
    //这里的核心方法是把类的构造方法设置为private
    private Singleton(){}

}

2.3:

这里再main方法内部再创建一个Singleton实例,发现会报出如下的报错提示,说明此时这个类遵循单例模式。

上面这种写法,创建单例的时机是在类加载的时候创建的,可以近似认为Java一创建就加载这个类 ,但是这样会有一个问题,在程序启动时,如果有很多这样的单例模式的类,那么就会扎堆初始化 ,这样可能会是服务器爆满,所以Java给出一种 " 懒汉模式 " 的单例模式,可以很好的解决在这个问题。

3.懒汉模式的单例模式(非必要,不创建new):

所谓懒汉模式就是可以有程序员决定创建单例类的时机,这里的创建时机由第一次调用getInstance()方法来决定;

java 复制代码
class Singletonlazy{
    //此处设置为null不会被初始化,只有new的时候才会被初始化;
    private static Singletonlazy Instance =null;
    public static Singletonlazy getInstance(){
        if(Instance==null){
            //对Instance进行初始化
            Instance = new Singletonlazy();
        }
        return Instance;
    }
    private Singletonlazy(){};
}

那么他们是否线程安全呢?

很明显是不安全的,这里我们先介绍一下Java的GC(垃圾回收)机制;

3.1GC机制:GC(Garbage Collection,垃圾回收) 是自动内存管理机制:识别并释放不再被引用的对象,避免内存泄漏与溢出,减少手动管理出错。

3.2:

再2步骤中t2将线程调度走后会创建(new)一次新对象,而当t1再次调度走线程后,会根据上下文再次创建(new)一次对象,这导致对象被创建了两次,随着第二次的创建完成,第一个对象就没有引用指向了,会被GC给释放掉,虽然最后结果确实是只剩下了一个对象,但我们仍然认为这里是存在bug的;

3.3:

这里根据之前学过的知识,可以对这个操作加锁,保证其原子性,就可以避免3.2的问题;

java 复制代码
 private static Singletonlazy Instance =null;
    public static Singletonlazy getInstance() {
        synchronized (Singletonlazy.class) {
            if (Instance == null) {
                //对Instance进行初始化
                Instance = new Singletonlazy();
            }
           
        }
          return Instance;
    }

这样加上锁之后,就可以保证在Instance实例在初始化之后,代码不涉及到修改,从而达到天然 " 线程安全 "。但是这样的代码只要线程一调度就自动加锁,导致开销很大性能变低,锁竞争很激烈,我们可以对代码做出下面的修改:在加锁之前先判定Instance是否被初始化,如果已经初始化过了,就不再进行加锁操作,这样大大降低了锁竞争的激烈程度。

DCL双重检查锁定:这也就是DCL双重检查锁定,第一层if判断是否需要加锁;第二层if判断是否需要进行初始化。

java 复制代码
class Singletonlazy{
    //此处设置为null不会被初始化,只有new的时候才会被初始化;
    private static Singletonlazy Instance =null;
    public static Singletonlazy getInstance() {
        if (Instance == null) {
            synchronized (Singletonlazy.class) {
                if (Instance == null) {
                    //对Instance进行初始化
                    Instance = new Singletonlazy();
                }
            }

        }
        return Instance;
    }
    private Singletonlazy(){};
}

举个例子来解释:

4.指令重排序引起的线程安全问题(只有这里存在这个问题!!!)

指令重排序也是编译器优化的一种手段,在保证逻辑不变的前提下,按照更优化的顺序来执行指令;

解决方案也很简单:加上volatile关键字,禁止编译器在读或写的时候进行优化。

java 复制代码
class Singletonlazy{
    //此处设置为null不会被初始化,只有new的时候才会被初始化;
    private volatile static Singletonlazy Instance =null;
    public static Singletonlazy getInstance() {
        if (Instance == null) {
            synchronized (Singletonlazy.class) {
                if (Instance == null) {
                    //对Instance进行初始化
                    Instance = new Singletonlazy();
                }
            }

        }
        return Instance;
    }
    private Singletonlazy(){};
}

这样,就构成了一个完整的单例模式!

相关推荐
qq_2975746711 小时前
设计模式系列文章(基础篇第19篇):中介者模式——封装交互关系,解耦网状依赖
设计模式·交互·中介者模式
AI大法师12 小时前
老牌媒体怎么从“出版物更新”走到“品牌系统升级”
大数据·人工智能·设计模式·新媒体运营
野生技术架构师13 小时前
Java 23 种设计模式:从踩坑到精通 —— 开篇及系列介绍
java·开发语言·设计模式
艾利克斯冰13 小时前
Java设计模式-创建型模式(更新完成)
设计模式
王_teacher13 小时前
23种设计模式之工厂模式
设计模式·软件工程·简单工厂模式·工厂方法模式·抽象工厂模式
geovindu14 小时前
python:Coroutines Pattern
开发语言·python·设计模式·协程模式
sycmancia15 小时前
Qt——模型视图设计模式
设计模式
玖玥拾1 天前
C/C++ 基础笔记(十一)类的进阶
c语言·c++·设计模式·
geovindu1 天前
go: Broadcast Pattern
开发语言·后端·设计模式·golang·广播模式
我爱cope1 天前
【Agent智能体23 | 规划-规划工作流】
人工智能·设计模式·语言模型·职场和发展