一. 使用多线程需要考虑的因素

提高效率:使用多线程就是为了充分利用CPU资源,提高任务的效率
线程安全:使用多线程最基本的就是保障线程安全问题

所以我们在设计多线程代码的时候就必须在满足线程安全的前提下尽可能的提高任务执行的效
故:
加锁细粒度化:加锁的代码少一点,让其他代码可以并发并行的执行

🍬考虑线程安全:

没有操作共享变量的代码没有安全问题
对共享变量的读,使用volatile修饰变量即可
对共享变量的写,使用synchronized加锁

🍊二. 单例模式

单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例
例如:DataSource(数据连接池),一个数据库只需要一个连接池对象

单例模式分为饿汉模式和懒汉模式

🌴1. 饿汉模式

饿汉模式是在类加载的时候就创建实例
这种方式是满足线程安全的(JVM内部使用了加锁,即多个线程调用静态方法,只有一个线程竞争到锁并且完成创建,只执行一次)

实现代码:

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

🌾2. 懒汉模式

懒汉模式是在类加载的时候不创建实例,第一次使用的时候才创建

实现代码:

public class Singleton {
    private static Singleton instance = null;
    private Singleton(){
 
    }
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

观察上述代码,在单线程下不存在线程安全问题,但是在多线程环境下存在安全问题吗? 

分析:
🍃当实例没有被创建的时候,如果有多个线程都调用getInstance方法,就可能创建多个实例,就存在线程安全问题 
🍃但是实例一旦创建好,后面线程调用getInstance方法就不会出现线程安全问题

结果:线程安全问题出现在首次创建实例的时候

🌵3. 懒汉模式(使用synchronized改进)

我们使用sychronized修饰,👁‍🗨️代码如下:

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

这样实现线程安全存在什么问题呢?

解析:
我们对方法使用synchronized修饰,也就是每次调用该方法的时候都会竞争锁,但是创建实例只需要创建一次,也就是创建实例后,再调用该方法还需要竞争锁释放锁

结果:虽然满足线程安全,但是效率低

4. 懒汉模式(使用双重校验锁改进)

在上述代码的基础上进行改动:

使用双重if判定,降低竞争锁频率
使用volatile修饰instance 

实现代码:

public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton(){
 
    }
    public static synchronized Singleton getInstance(){
        if(instance == null){ //外层的if判断:如果实例被创建直接return,不让线程再继续竞争锁
            //在没有创建实例时,多个线程已经进入if判断了
            //一个线程竞争到锁,其他线程阻塞等待
            synchronized (Singleton.class) {
                //内层的if判断,目的是让竞争失败的锁如果再次竞争成功的话判断实例是否被创建,创建释放锁return,没有则创建
                if(instance == null){ 
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

🍬对双重if的解析:

🍂外层的if判断:实例只是被创建一次,当实例已经被创建好了就不要后续操作,直接return返回
🍂内层的if判断:实例未被创建时,多个线程同时竞争锁,只有一个线程竞争成功并创建实例,其他竞争失败的线程就会阻塞等待,当第一线程释放锁后,这些竞争失败的线程就会继续竞争,但是实例已经创建好了,所以需要再次进行if判断 

画图分析,如下所示:

三. volatile的原理 

volatile保证了可见性,有序性,在Java层面看,volatile是无锁操作,多个线程对volatile修饰的变量进行读可以并发并行执行,和无锁执行效率差不多

volatile修饰的变量中,CPU使用了缓存一致性协议来保证读取的都是最新的主存数据 

缓存一致性:如果有别的线程修改了volatile修饰的变量,就会把CPU缓存中的变量置为无效,要操作这个变量就要从主存中重新读取

🍏四. volatile的扩展问题(了解)

🍬如果说volatile不保证有序性,双重校验锁的写法是否有问题?

关于new对象按顺序分为3条指令:

🍁(1) 分配对象的内存空间
🍁(2) 实例化对象
🍁(3) 赋值给变量

正常的执行顺序为(1)(2)(3),JVM可能会优化进行重排序后的顺序为(1)(3)(2)

这个重排序的结果可能导致分配内存空间后,对象还没有实例化完成,就完成了赋值
在这个错误的赋值后,instance==null不成立,线程就会拿着未完成实例化的instance,使用它的属性和方法就会出错

使用volatile保证有序性后:

线程在new对象时不管(1)(2)(3)是什么顺序,后续线程拿到的instance是已经实例化完成的
CPU里边,基于volatile变量操作是有CPU级别的加锁机制(它保证(1)(2)(3)全部执行完,写回主存,再执行其他线程对该变量的操作)

 到此这篇关于Java单例模式中的线程安全问题的文章就介绍到这了,更多相关Java单例模式线程安全内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java单例模式中的线程安全问题的更多相关文章

  1. ios – Realm Swift模型是否分开?

    我是iOS和Swift的世界的新手,正在开发一个新的应用程序,我想使用领域来保持持久性.我的代码中已经有我的服务访问和填充HTTPAPI端点的实体.现在我想坚持一些实体,并且想要建议我是否应该为每个实体创建新的领域特定的模型,以便从领域读写.或者我应该将我现有的所有简单的Swift实体转换为国家实体.起初,我感觉到错误,因为我将在我的应用程序中传递RealmEntities,而不是在持久层中.但是

  2. 在Swift中应用Grand Central Dispatch(上

    在这两篇教程中,你会学到GCD的来龙去脉。起步libdispatch是Apple所提供的在IOS和OSX上进行并发编程的库,而GCD正是它市场化的名字。Swift中的闭包和OC中的块类似甚至于他们几乎就是可交换使用的。但OC中的块可以安全的替换成Swift中的闭包。再一次,这完全取决于GCD。QoS等级表示了提交任务的意图,使得GCD可以决定如何制定优先级。QOS_CLASS_USER_INteraCTIVE:userinteractive等级表示任务需要被立即执行以提供好的用户体验。

  3. 【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2

    所有的dispatchqueues自身都是线程安全的。dispatch_sync把任务添加到对应队列并等待其完成后再继续执行当前任务,容易造成死锁,或阻塞当前任务。dispatch_after指定时间后把任务添加到队列中。效果就像是延时后的dispatch_async。而array和dictionary在swift中是以struct的形式实现的,所以以上的读操作返回的是一个副本。在GCD中使用dispatchbarrier来解决这个问题。dispatchbarrier是一组方法,它们都已顺序化的方式来结合

  4. Swift 单例模式实现及类方法

    在开始单例模式之前我们先讲一下类方法。应为单例模式需要依靠类方法来实现首先,我们知道类方法就是直接使用类名来调用,无需生成对象,再去调用。比如我们属性的NSUserdefault就是一个单例接下来我们实现一个类,用来共享数据。

  5. swift详解之十六-----------GCD基础部分

    当你了解了调度队列如何为你自己代码的不同部分提供线程安全后,GCD的优点就是显而易见的。这完全取决于GCD。这个队列就是用于发生消息给UIView或发送通知的。GCD的“艺术”归结为选择合适的队列来调度函数以提交你的工作。

  6. Swift多种实现单例写法

    学习Swift的单例写法,以下提供了5种方式,一起来看看吧!

  7. Swift中单例的集中创建方式

    单例模式是设计模式中最简单的一种,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象。下面看看swift中创建单例的集中方式:1、一个句话搞定,所有用到的地方都是同一个2、3、4.swift还在初学阶段,如果有不对的地方,还望指出。

  8. Swift百万线程攻破单例Singleton)模式

    回到技术问题,我们想要更多的线程访问SwiftSingleton.shared方法,只要先准备好所有的线程,然后发一个信号,让他们同时去访问这个方法就可以了。测试结果:通过2、使用GCD技术实现的单例模式dispatch_once{Inner.instance=SwiftSingleton()}returnInner.instance!

  9. Swift线程安全详解-概念,三种锁,死锁,Atomic,synchronized

    研究了下,是线程安全问题。UIKit以及Fundation事实上,大多数Cocoa提供的Api都不是线程安全的,尤其是与UI相关的UIKit,只能在主线程上操作。需要线程安全的时候,开发者自己维护就可以了。用来加锁,解锁。关于ObjectiveC参考这篇文章Atomic一个非Atomic的属性在非ARC的时候像这样可以看到,如果在多线程同时set的情况下,可能会造成release两次。Property的Runtime对应的C代码为可以看到,如果是nonatomic的,synchronized可以看看这个S

  10. Swift中的单例模式

    三种方法实现单例模式,线程安全1.全局变量2.结构3.dispatch_once1.全局变量:这里使用了全局变量而非类变量,是因为不支持类变量2.结构:与类不同,结构可以定义静态变量,通过使用此方法,将静态变量升级为类变量3.dispatch_once:

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部