线程安全之单例模式

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


前言

这篇文章,我们会介绍一下单例模式,但这里的单例模式,不是我们所说的设计模式,当然听到设计模式,大家一定都说,我当然知道设计模式了,有23种呢?一下子一顿输出,当然我这里说的单例模式还是跟设计模式有一些区别的,当然我不做概述,因为我也没咋个去了解过设计模式,我把大家拉回来,什么是多线程对的单例模式呢?看完我以下的解释相信你会明白的.


一.什么是单例模式

多线程环境下的单例模式需要保证只有一个实例对象被创建,并且可以在多线程环境下安全地访问该实例。
概念性的东西就是这样,把这段话,反复读,相信你也读不出来什么结果.简单来说的话,就是我现在只能娶一个老婆,不能娶多个老婆一样的意思,如果我娶多个老婆的话,在现在看来就是犯法的,当然了,小伙伴们,可不敢这样想,我想要后宫佳丽3000,这种想想就行了,哈哈
再来说说,在java中的单例模式的情况,Java 中的单例模式,借助java语法,保证某个类,只能够创建出一个实例,而不能new 多次.下面我们继续来看看在java中如何实现单例模式.


二.在java中的单例模式

怎么说呢?在Java中实现单例模式主要有以下几种:

  1. 饿汉式
  2. 懒汉式
  3. 双重检查锁
  4. 静态内部类
  5. 枚举

当然,我们或许没列举完,如果有其他的,也请各位小友,补充以下,当然我也不会全部介绍完,只会详细的介绍饿汉式和懒汉式.

2.1 饿汉式的介绍

饿汉式,大家通常想到饿这个词的,通常就会跟狼吞虎咽这个词语联系到一起,当然,我举个简单的例子,就拿取快递来说吧,你买了8件快递,今天到了2件,你马上就会去取快递.这就是饿汉式,遇事就马上下决定,真男人,绝不退缩,突出一个猛.

了解完什么是饿汉式,我们来看一看,饿汉式的代码实现.

//把这个类设置成单例的
public class Singleton {
    private static Singleton instant = new Singleton();

    //获取实例的方法
    public static Singleton getInstance() {
        return instant;
    }

    //禁止外部new实例
    private Singleton() {
    }

    ;
}
class  ThreadDemo16{
     public static void main(String[] args) {
         //此时的s1
         // 此时 s1 和 s2 是同一个对象!!
         Singleton s1 = Singleton.getInstance();
         Singleton s2 = Singleton.getInstance();
         System.out.println(s1 ==s2);
     }
 }


简单来说,我们是怎么去实现这个饿汉式的单例模式的呢,主要进行了俩点,你注意看
我们这一句:

    private static Singleton instant = new Singleton();
    是类内部把实例创建好.

另外一个步骤就是,我们在创建对象,进行实例化的时候,我们吧构造方法私有化.

    private Singleton() {
    }

这俩个步骤,我们自然就实现了单例模式,保证只有一个实例对象被创建.
当然我们既然谈到线程,我们自然就要讨论一个问题,你觉的.我们饿汉式的方式是线程安全的吗?如果你看过,我写的那个线程安全的类别时候,我们就知道,是否会发生线程安全的问题了.
单例线程,多线程,单例模式,java

总的来说,这上面几种情况都是没有出现的,因此我们初步判断饿汉式是线程安全的,但是也不完全是,有些资料说会在类加载的时候就创建实例,如果该实例很大或者初始化过程很耗时,会造成资源浪费。这点值不值得考究,我就不知道了,但从表面上来说,我们并没有从饿汉式中看出什么端倪.


2.2 懒汉式的介绍

懒汉式,我们自然就注重一个懒字,那我们为啥要叫懒汉式呢?其实吧,我再拿快递的例子去解释一下,假设你买了八个东西,但突然有一个快递到了,但是你不会立刻去取,你要等到它们都到了,一起去取,这就是懒汉式,理解了这个概念之后,我们就看一下java代码的概念.

import com.sun.org.apache.regexp.internal.RE;

class SingleLazy{
    private static   SingleLazy instance=null;
    private  SingleLazy(){}
    public static  SingleLazy getInstance(){
        if (instance == null){
            instance=new  SingleLazy();
        }
        return instance;
    }
}
public class ThreadDemo18 {
    public static void main(String[] args) {
        SingleLazy s1 =  SingleLazy.getInstance();
        SingleLazy s2 =  SingleLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

看到了懒汉式的代码.你就可以看出来,跟饿汉式的区别是,懒汉式,就是非必要时候,不创建对象.具体是哪一句呢?我这里给你举出来,你可以思考一下,我说的是不是对的.

    public static  SingleLazy getInstance(){
        if (instance == null){
            instance=new  SingleLazy();
        }
        return instance;
        这里加了判断语句,非必要的时候,不创建对象.

当然我们居然说了这种单例模式后,你来思考一下,它是不是线程安全的呢?其实简单来说,它是不是线程安全的,判断还是很简单的,因为你这样看,我们这里有判断条件,有判断条件,就涉及到赋值判断操作,你想象多线程环境下,线程1会对起进行判断,线程2也会进行判断,万一判断不是同时进行的,那不就整了个乌龙时间了吗?另外我们再对照着线程不安全的情况,仔细的思考一下.
单例线程,多线程,单例模式,java
所以说,懒汉式会破坏单例模式的原则,导致线程不安全,但是有没有方式解决呢?答案是有的,接下来我就会解释,我们怎么去避免这个问题,所以大家还是不要心急,细细听我道来.

三 懒汉式的单例模式,线程不安全的解决方式

3.1 造成线程不安全的原因

大家不要嫌我啰嗦,我再说明一下原因,这样我们才好说出解决策略
当多个线程同时访问getInstance()方法时,可能会出现竞态条件,即两个线程同时执行了if (instance == null)语句,导致创建了两个实例。这种情况下,线程单例模式就会失效。这个东西,就是我们说的会导致的线程安全问题.
接下来我们来提供一个解决方案.

3.2 解决方案

当然哈,我们开始说了是线程安全的原因,就是在创建对象的那一步,出现了,多线程竞争的关系,那么我们是不是可以直接在创建对象的那一步,加锁.代码如下:

    public static  SingleLazy getInstance(){
   		 synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        return instance;
    }

当然,我们这一步的操作,其实是保证了,创建对象一开始的原子性.
当然,加锁的操作,我们还是谨慎的,由于加锁的机制,当多个线程同时调用该方法时,只能有一个线程进入临界区创建实例,其他线程则被阻塞,可能会导致性能瓶颈。因此我们要避免就是非必要情况不加锁,就拿当前的这个例子来说,我们首次进入这个代码段的时候,如果对象已经创建了,我们就不进行加锁,对象没有创建,我们就加锁,因此外面就必须再嵌套一层循环.

    public static SingletonLazy getInstance() {
        // 这个条件, 判定是否要加锁. 如果对象已经有了, 就不必加锁了, 此时本身就是线程安全的.
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

当然大家不要被这个这俩个if条件所迷惑,一个避免过度加锁,第二个if判断就是避免多线程对其进行同时操作.
到这里,大家觉得我们线程安全的问题结束了吗?其实并没有,因为我们创建对象的时候,还会发生指令重排序.因此我们还必须进行下一步的优化,代码如下所示:

lass SingletonLazy {
    volatile private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {
        // 这个条件, 判定是否要加锁. 如果对象已经有了, 就不必加锁了, 此时本身就是线程安全的.
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

我们来解释一下为什么会出现指令重排序问题,如果你看过我的上一篇线程安全的粗略详解,你就大概明白了,这个问题,当然我们,再来说一下,我们这里为啥会出现现在的问题.
因为创建实例的代码通常会被拆分成多个指令,包括分配内存空间、初始化对象、将对象赋值给变量等。如果编译器或处理器为了提高性能而进行指令重排序,可能会导致上述指令的执行顺序与代码的顺序不一致。这样,如果一个线程在另一个线程完成了对象赋值操作之前执行了if语句的第一个条件判断,就会错误地认为实例已经被创建,从而导致程序出错。
为了解决这个问题,可以使用volatile关键字来修饰单例实例变量,这样可以保证线程对该变量的读写操作都是原子的,并且禁止指令重排序

3.3 总结

最后让我们小小的总结一下,我们解决的办法
1.加锁,把if 和new变成原子操作.
⒉.双重 if,减少不必要的加锁操作
3.使用volatile禁止指令重排序,保证后续线程肯定拿到的是完整对象文章来源地址https://www.toymoban.com/news/detail-738478.html

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

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

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

相关文章

  • 【Java 设计模式】创建型之单例模式

    在软件开发中,单例模式是一种常见的设计模式, 它确保一个类只有一个实例,并提供一个全局访问点 。单例模式在需要控制某些资源,如数据库连接池、线程池等共享资源的情况下非常有用。在本文中,我们将介绍 Java 设计模式中的单例模式,了解其实现方式、使用场景

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

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

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

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

    2024年01月15日
    浏览(55)
  • Java23种设计模式-创建型模式之单例模式

    单例模式 (Singleton Pattern):通过单例模式的方法创建的 类在当前进程中只有一个实例 (根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例),该类负责 创建自己的对象 ,同时 确保只有单个对象 被创建。 注 : 1、单例类 只能 有 一个实例 。

    2024年04月26日
    浏览(54)
  • 手写java设计模式之单例模式,附源码解读

    在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处: 1、减少类的频繁创建,减少使用频繁使用new创建实例,减少GC压力。 2、某些应用场景下,使用单例模式,保证整个系统中只会创建一个类。 单例模式分两种:饿汉模式和懒汉模

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

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

    2024年02月02日
    浏览(40)
  • Java中static的应用之单例模式

    单例模式是一种创建对象的设计模式,它保证一个类只有一个实例,并提供一个全局访问点。由于单例模式只允许存在一个实例,因此它可以节省系统资源并提高程序的性能。在许多情况下,单例模式在应用程序中都是非常有用的,例如数据库连接、日志记录、配置文件等。

    2024年02月12日
    浏览(46)
  • Java课堂|独一无二的事物(设计模式之单例模式)

    本文主要讲述 单例模式 ,文中使用通俗易懂的案例,使你更好的学习本章知识点并理解原理,做到有道无术。 单例模式是23种设计模式中 创建型模式 的一种,通过单例模式的方法创建的类在当前进程或者线程中只有一个实例。单例模式有两种比较常见的实现方式: 饿汉式

    2024年02月07日
    浏览(44)
  • Java设计模式之单例模式详解--独一无二的事物

    本文主要讲述 单例模式 ,文中使用通俗易懂的案例,使你更好的学习本章知识点并理解原理,做到有道无术。 单例模式是23种设计模式中 创建型模式 的一种,通过单例模式的方法创建的类在当前进程或者线程中只有一个实例。单例模式有两种比较常见的实现方式: 饿汉式

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

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

    2024年02月20日
    浏览(46)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包