线程安全的单例模式(代码片段)

yangyongjie yangyongjie     2023-04-09     125

关键词:

  双重检查锁与延迟初始化(懒汉式)

    在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销,在使用这些对象时才进行初始化。延迟初始化需要注意线程安全

  问题,否则就容易出现问题。

    单例模式在获取实例的方法中,若只判断实例是否为null,是则创建对象,否则获取对象。这种方法在多线程执行的时候必然会有线程安全问题。若获取

  实例方法加synchronized关键字,则能实现线程同步解决线程安全问题,但是多线程下频繁调用会造成巨大的性能开销。

    1、双重检查锁及其错误根源

      双重检查锁是常见的延迟初始化技术,初衷是用它来降低同步的开销,但是它是错误的用法。

    双重检查锁代码:

public class DoubleCheckLock 
    private static Instance instance;
    public static Instance getInstance()
        // 第一次检查
        if(instance==null)
            // 第一次检查为null再进行加锁,降低同步带来的性能开销
            synchronized (DoubleCheckLock.class)
                // 第二次检查
                if(instance==null)
                    // 问题出在此处
                    instance=new Instance();
                
            
        
        return instance;
    

 

     错误根源:

      当第一次检查时,读取instance不为null时,instance引用的对象可能还没有完成初始化!原因在于多线程下的重排序。

      instance=new Instance();  分为三步

        1)分配对象的内存空间

        2)初始化对象

        3)设置instance引用指向刚分配的内存地址

      但是在一些编译器上(如JIT),2)和3)可能会发生重排序。

      Java规范保证了重排序不会改变单线程内的程序执行结果。但是在多线程下,若线程A在执行instance=new Instance();时发生了重排序,先执行了3),

    这时候线程B刚好获取到了instance不为null,接着去访问对象。但是这个时候线程A还未执行2),即还没被线程A初始化,那么这个时候线程B得到的就是

    一个还没有初始化的对象。

      解决方案:

        (1)不允许2)和3)重排序

        (2)允许2)和3)重排序,但不允许其他线程“看到”这个重排序

    

    2、基于volatile的解决方案

     通过将instance声明为volatile型来禁止2)和3)之间的重排序

public class VolatileDoubleCheckLock 
    // 将instance声明为volatile型
    private volatile static Instance instance;
    public static Instance getInstance()
        // 第一次检查
        if(instance==null)
            // 第一次检查为null再进行加锁,降低同步带来的性能开销
            synchronized (VolatileDoubleCheckLock.class)
                // 第二次检查
                if(instance==null)
                    // 多线程下将禁止2)和3)之间的重排序
                    instance=new Instance();
                
            
        
        return instance;
    

 

    3、基于类初始化的解决方案

     JVM在类的初始化阶段(即被Class加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁(Class对象的初始化锁)。这个锁可以同步多个线程对一个类的初始化。

public class InstanceFactory 
    private static class InstanceHolder
        public static Instance instance=new Instance();
    
    
    public static Instance getInstance()
        return InstanceHolder.instance;
    

    在多线程下,第一个执行getInstance方法的线程会先初始化InstanceHolder类。多个线程的情况下也只有一个线程能获取到InstanceHolder类的Class对象的初始化锁。第一个获取到nstanceHolder类的Class对象的初始化锁的线程将初始化InstanceHolder类中的静态属性instance。根据happens-before关系,其他线程将知道InstanceHolder类已被初始化,将结束初始化过程直接访问InstanceHolder。

 

多线程阻塞队列定时器线程安全的单例模式的原理及实现(代码片段)

文章目录1.线程安全版本的单例模式1.1单例模式介绍1.2实现线程安全版本的懒汉模式2.阻塞队列2.1阻塞队列介绍2.2标准库中的阻塞队列2.3实现阻塞队列2.4生产者消费者模型3.定时器3.1定时器介绍3.2标准库中的定时器3.3实现定时器1.... 查看详情

多线程阻塞队列定时器线程安全的单例模式的原理及实现(代码片段)

文章目录1.线程安全版本的单例模式1.1单例模式介绍1.2实现线程安全版本的懒汉模式2.阻塞队列2.1阻塞队列介绍2.2标准库中的阻塞队列2.3实现阻塞队列2.4生产者消费者模型3.定时器3.1定时器介绍3.2标准库中的定时器3.3实现定时器1.... 查看详情

多线程实现单例模式(饿汉懒汉)实现线程安全的单例模式(双重效验锁)(代码片段)

...比饿汉模式好。主要因为懒汉模式的效率更高1.饿汉模式(线程安全)//饿汉模 查看详情

推荐使用的三种无线程安全问题的单例模式(代码片段)

1、饿汉式publicclassSingletonprivatestaticSingletoninstance=newSingleton();privateSingleton()publicstaticSingletongetInstance()returninstance;2、懒汉式的双重检查锁定publicclassSingleton2privateSingleton2()p 查看详情

线程安全的单例模式(代码片段)

... 双重检查锁与延迟初始化(懒汉式)    在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销,在使用这些对象时才进行初始化。延迟初始化需要注意线程安全  问题,否则就容易出现问... 查看详情

5种常见的单例模式(代码片段)

....1版本1:直接私有化构造函数。       缺点:非线程安全,当多个线程同时运行到if(instance==null)时,会创建多个对象。1publicclassSingleton2privatestaticSingletoninstance;3privateSingleton()456publicstaticSingletongetInstance() 查看详情

5java单例模式(线程安全性)(代码片段)

java中的单例模式是一种广泛使用的设计模式。单例模式的主要作用是保证在java程序中,某个类只有有一个实例存在单例模式1、饿汉模式publicclassSingletonprivatestaticSingletoninstance=newSingleton();privateSingleton()publicstaticSingletonnewIns... 查看详情

线程安全的单例模式

1.全局变量的缺点:必须在程序一开始就创建好对象,如果程序在这次的执行过程中又一直没用到它,就非常耗费资源。 2. 经典的单例模式实现:Java代码publicclassSingleton{//用一个静态变量来记录Singleton类的唯一实例privates... 查看详情

简单的单例模式其实也不简单(代码片段)

...果要深究,小小的单例模式可以牵扯到很多东西,比如多线程是否安全,是否懒加载,性能等等。还有你知道几种单例模式的写法呢?如何防止反射破坏单例模式?今天,我就花一章内容来说说单例模式。关于单例模式的概念,... 查看详情

java实现线程安全的单例模式

一、平时使用的软件中,例如回收站、线程池、文件系统等,都只有一个实例,这些都是单例模式的典型应用。  单例模式:确保某个类只有一个实例,并提供一个全局访问点来访问这个实例。  单例模式有三个要点:  ... 查看详情

多线程下的单例模式详解(代码片段)

...例模式(1)单例模式简介(2)实现方式①饿汉式②懒汉式2.多线程下的单例模式(1)Synchronized(2)双重检查锁(3)双重检查锁+Volatile补充知识点1.单例模式(1)单例模式简介单例模式的作用单例模式是为了一个类的示例只有一个,并且... 查看详情

5单例模式(代码片段)

  单例模式定义:确保一个类只有一个实例,并提供线程安全的访问点。    以下介绍6中线程安全的单例模式立即加载模式/饿汉模式通过反射的方式可以获得多个实例publicclassSingleton1privatestaticSingleton1singleton1=newSingleton1()... 查看详情

实现线程安全的单例模式

一、双检查锁机制packagesingleton;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/***双检查锁机制--单例模式*Createdbydaizengjieon2017/8/29.*/publicclassMySingleton{privatestaticfinalLoggerlogger=LoggerFactory.getL 查看详情

高并发下线程安全的单例模式

...现方式,你是否都了解呢?高并发下如何保证单例模式的线程安全性呢?如何保证序列化后的单例对象在反序列化后任然是单例的呢?这些问题在看了本文之后都会一一的告诉你答案,赶快来阅读吧!什么是单例模式?在文 查看详情

linux线程池|线程安全的单例模式|stl智能指针与线程安全|读者写者问题(代码片段)

...却得不到结果的日子,我们把它叫做扎根目录👉线程池👈什么是线程池线程池的优点线程池的应用场景:线程池的实现👉日志功能的实现👈👉线程安全的单例模式👈什么是单例模式饿汉方式懒... 查看详情

c++的单例模式与线程安全单例模式(懒汉/饿汉)

1教科书里的单例模式  我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个privatestatic的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法... 查看详情

如何实现一个线程安全的单例字典类进行缓存?(代码片段)

我试图实现一个线程安全字典单例类用于缓存目的。namespaceSingletomDictpublicsealedclassMySingleton:IDisposableprivatestaticreadonlyLazy<MySingleton>coll=newLazy<MySingleton>(()=>newMySingleton());privatestati 查看详情

java基础——线程安全的单例模式懒汉式

packagesavesingleton;/*使用同步将单例模式中的懒汉式改写成线程安全的@authorzsben@create2020-01-0322:22*/classBank{privateBank(){}privatestaticBankinstance=null;/*publicstaticsynchronizedBankgetInstance(){if(instance==null){ 查看详情