【Java EE初阶六】多线程案例(单例模式)

这篇具有很好参考价值的文章主要介绍了【Java EE初阶六】多线程案例(单例模式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 单例模式

        单例模式是一种设计模式,设计模式是我们必须要掌握的一个技能;

1.1 关于框架和设计模式

        设计模式是软性的规定,且框架是硬性的规定,这些都是技术大佬已经设计好的;

        一般来说设计模式有很多种,且不同的语言会有不同的设计模式,(同时设计模式也可以理解为对编程语言的一种补充

1.2 细说单例模式

        单例 = 单个实例(对象);

        某个类,在一个线程中,只应该创建一个实例化对象(原则上不应该有多个),这时就使用单例模式,如此可以对我们的代码进行一个更严格的校验和检查。

        保证对象唯一性的方法:

        方法一,可以通过“协议约束”,写一个文档,规定这个类只能有唯一的实例,程序员在接手这个代码时,就会发现这个文档已经进行约定,其中的规定约束着程序员在创建对象时,时刻注意只能创建一个对象。

        方法二:从机器入手;让机器帮我们检查,我们期望让机器帮我们对代码中指定的类,创建类的实例个数进行检查、校验,当创建的实例个数超过我们期望个数,就编译报错。其中单例模式就是已经设计好的套路,可以实现这种预期效果。

        关于单例模式代码实现的基本方式有两种:饿汉模式和懒汉模式;

2. 饿汉模式

        饿汉模式是指创建实例的时期非常早;在类加载的时候,程序一启动,就已经创建好实例了,使用 “饿汉”这个词,就是形容创建实例非常迫切,非常早。单例模式代码如下:

class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
    private Singleton(){ }
}
public class TestDemo4 {
    public static void main(String[] args) {
        Singleton singleton = new Singleton();
    }
}

        当我们运行该代码时,系统就会报错,接下来我们详细的分析一下此处的代码; 

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

        这样,如果我们想new一个Singleton对象,也new不了,同时不管我们用getInstance获取多少次实例,获取的对象都是同一个对象,代码如下:

package thread;

// 就期望这个类只能有唯一的实例 (一个进程中)
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 s = new Singleton();
        Singleton s = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s == s2);
    }
}

        结果如下:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

3. 懒汉模式

        和饿汉模式不一样的是,懒汉模式创建实例的时机比较晚,没饿汉创建实例那么迫切,只有第一次使用这个类时,才会创建实例,代码如下:

class SingletonLazy {
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        if(instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
    private SingletonLazy() { }
}
public class TestDemo5 {
    public static void main(String[] args) {
 
    }
}

        下面为代码图解分析:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

        和饿汉模式的区别就是没那么迫切创建实例,等需要调用这个类的时候才创建一个实例,而饿汉模式是有了这个类就创建出实例。

        懒汉模式的优点:有的程序,要在一定条件下,才需要进行相关的操作,有时候不满足这个条件,也就不需要完成这个操作了,如此哦·就把这个操作省下来了。

4. 两种模式关于线程安全

4.1 饿汉模式

        线程安全;

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

        对于饿汉模式来说,上图所示通过调用getinstance方法来返回instance对象,本质上来说是读操作;

        当有多个线程,同时并发执行,调用getInstance方法,取instance,这时线程是安全的,因为只涉及到读,多线程读取同一个变量,是线程安全的。而instance很早之前就已经创建好了,不会修改它,一直也只有这一个实例,也不涉及写的操作。

4.2 懒汉模式

        线程不安全;

        在懒汉模式中,条件判定和返回时是读操作,new一个对象是写操作;

       我们只有调用getInstance方法后,就会创建出实例来,如果多个线程同时调用这个方法,此时SingletonLazy类里面的instance都为null,那么这些线程都会new对象,就会创建多个实例。这时,就不符合我们单例模式的预期了,所以,这个代码是线程不安全的。

        线程不安全的直接原因,就是 “写” 操作不是原子的。

4.3 解决懒汉模式的线程安全问题

4.3.1 把写操作打包成原子

        因为多线程并发执行的时候,可能读到的都是instance == null,所以会创建多个实例,那我们就给它加锁,让它在创建实例的时候,只能创建一个,加锁代码如下:

class SingletonLazy {
    private static Object locker = new Object();
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        synchronized (locker) {
            if(instance == null) {
                instance = new SingletonLazy();
            }
        }
        return instance;
    }
    private SingletonLazy() { }
}

        以上操作虽然将写操作打包成了一个原子,但是新的问题也出现了;

4.3.2 去除冗余操作

         上述操作加上了还是有问题:如果已经创建出实例了,我们还有加锁来判断它是不是null吗,加锁这些操作也是要消耗硬件资源的,没有必要为此浪费资源空间,如果已经不是null了,我们就想让它直接返回,不再进行加锁操作,代码修改如下:

class SingletonLazy {
    private static Object locker = new Object();
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy() { }
}

        代码图解分析两个判断语句的是目的意义:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

4.3.3 指令重排序的问题

        指令重排序:指令重排序也是编译器的一种优化,在保证原代码的逻辑不变,调整原代码的指令执行顺序,从而让程序的执行效率提高。

        保证原代码的逻辑不变,改变原有指令的顺序,从而提高代码的执行效率,其中这个代码,就存在着指令重排序的优化,如下图代码:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

该语句原本指令执行顺序:

        1、去内存申请一段空间

        2、在这个内存中调用构造方法,创建实例

        3、从内存中取出地址,赋值给这个实例instance。

指令重排序后的顺序:1, 3 , 2;按照指令重排序后的代码执行逻辑就变成了下面所示:

        假设有两个线程,现在执行顺序如下图所示:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

        因为指令重排序后,先去内存申请一段空间,然后是赋值给instance,那这时,instance就不是null了,第二个线程不会进入到if语句了,直接返回instance,可是instance还没有创建出实例,这样返回肯定是有问题的,如此也就线程不安全了。

        解决方案:

        给instance这个变量,加volatile修饰,强制取消编译器的优化,不能指令重排序,同时也排除了内存可见性的问题。

        加volatile后的代码如下:


 

class SingletonLazy {
    private static Object locker = new Object();
    private static volatile SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy() { }
}

        至此,我们才算解决掉懒汉模式关于线程安全的所有问题;

4.4 懒汉模式线程安全的代码

package thread;

// 懒汉的方式实现单例模式.
class SingletonLazy {
    // 这个引用指向唯一实例. 这个引用先初始化为 null, 而不是立即创建实例
    private volatile static SingletonLazy instance = null;
    private static Object locker = new Object();
    public static SingletonLazy getInstance() {
        // 如果 Instance 为 null, 就说明是首次调用, 首次调用就需要考虑线程安全问题, 就要加锁.
        // 如果非 null, 就说明是后续的调用, 就不必加锁了.
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy() { }
}

public class ThreadDemo27 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

        结果如下:

【Java EE初阶六】多线程案例(单例模式),JAVA EE 初阶,单例模式,java-ee,java

ps:本次的内容就到这里了,如果感兴趣的话,就请一键三连哦!!!文章来源地址https://www.toymoban.com/news/detail-774048.html

到了这里,关于【Java EE初阶六】多线程案例(单例模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列

    目录 1、单例模式 1.1、饿汉模式 2.1、懒汉模式  2、阻塞队列 2.1、BlockingQueue 阻塞队列数据结构 对框架和设计模式的简单理解就是,这两者都是“大佬”设计出来的,让即使是一个代码写的不太好的“菜鸡程序员”也能写出还可以的代码。 设计模式也可以认为是对编程语言语

    2024年03月23日
    浏览(90)
  • 【Java EE初阶三 】线程的状态与安全(下)

             线程安全 : 某个代码,不管它是单个线程执行,还是多个线程执行,都不会产生bug,这个情况就成为“线程安全”。          线程不安全 : 某个代码,它单个线程执行,不会产生bug,但是多个线程执行,就会产生bug,这个情况就成为 “线程不安全”,或者

    2024年02月03日
    浏览(44)
  • 【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

    🍎 个人博客: 个人主页 🏆 个人专栏: JAVA ⛳️   功不唐捐,玉汝于成 目录 前言 正文 懒汉式(Lazy Initialization): 双重检查锁定(Double-Checked Locking): 结语 我的其他博客 在软件设计中,单例设计模式是一种重要的设计思想,它确保了一个类只有一个实例,并提供了一

    2024年01月15日
    浏览(55)
  • Java多线程基础-8:单例模式及其线程安全问题

    单例模式是经典的设计模式之一。什么是设计模式?代码的设计模式类似于棋谱,棋谱就是一些下棋的固定套路,是前人总结出来的一些固定的打法。依照棋谱来下棋,不说能下得非常好,但至少是有迹可循,不会下得很糟糕。代码的设计模式也是一样。 设计模式,就是软件

    2024年02月05日
    浏览(50)
  • Java 枚举实现单例模式,线程安全又优雅!

    这种DCL写法的优点:不仅线程安全,而且延迟加载。 1.1 为什么要double check?去掉第二次check行不行? 当然不行,当2个线程同时执行getInstance方法时,都会执行第一个if判断,由于锁机制的存在,会有一个线程先进入同步语句,而另一个线程等待,当第一个线程执行了 new Sin

    2024年02月02日
    浏览(40)
  • Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)

    设计模式就是软件开发中的“棋谱”,软件开发中也有很多常见的 “问题场景”。针对这些问题场景,大佬们总结出了一些固定的套路。按照这些套路来实现代码可能不会很好,但至少不会很差。当前阶段我们需要掌握两种设计模式: (1)单例模式 (2)工厂模式 概念/特征

    2024年02月09日
    浏览(58)
  • 【Java中23种设计模式-单例模式2--懒汉式2线程安全】

    加油,新时代打工人! 简单粗暴,学习Java设计模式。 23种设计模式定义介绍 Java中23种设计模式-单例模式 Java中23种设计模式-单例模式2–懒汉式线程不安全 通过运行结果看,两个线程的地址值是相同的,说明内存空间里,创建了一个对象。

    2024年02月20日
    浏览(46)
  • Java设计模式之创建型-单例模式(UML类图+案例分析)

    目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 4.1、饿汉模式 4.2、懒汉模式(线程不安全) 4.3、懒汉模式(线程安全) 4.4、双重检索模式 4.5、静态内部类 4.6、枚举  五、总结 单例模式确保一个类只有一个实例,提供一个全局访问点。一般实现方式是把构造函

    2024年02月13日
    浏览(46)
  • 【JavaEE初阶】多线程(三)volatile wait notify关键字 单例模式

    摄影分享~~ 以上代码运行的结果可能是输入1后,t1这个线程并没有结束。而是一直在while中循环。而t2线程已经执行完了。 以上情况,就叫做 内存可见性问题 这里使用汇编来理解,大概分为两步操作: load,把内存中flag的值,读到寄存器中。 cmp,把寄存器中的值,和0进行比

    2023年04月25日
    浏览(42)
  • 【多线程案例】单例模式

    单例模式是设计模式的一种,先谈谈什么是设计模式? 大家应该都知道棋谱、剑谱之类的,就是一些“高手”在经历过长期的累计之后,更具经验写出的具有固定套路的处理“方法”,只要按照这个套路来,在对局之中必然是不会吃亏的,甚至能够一招制敌。 那么在我们日

    2024年02月07日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包