精华内容
下载资源
问答
  • 首页专栏java文章详情0单例模式之双重校验锁的优缺点文若杂谈发布于 今天 03:26双重校验锁代码public class DoubleLock {private static DoubleLock doubleLock;private DoubleLock(){}public static DoubleLock ...

    首页

    专栏

    java

    文章详情

    0

    单例模式之双重校验锁的优缺点

    114551.html文若杂谈发布于 今天 03:26

    双重校验锁代码

    public class DoubleLock {

    private static DoubleLock doubleLock;

    private DoubleLock(){

    }

    public static DoubleLock getInstance(){

    if (doubleLock == null){

    synchronized (DoubleLock.class){

    if (doubleLock == null){

    doubleLock = new DoubleLock();

    }

    }

    }

    return doubleLock;

    }

    }

    优点

    安全且在多线程情况下能保持高性能,第一个if判断避免了其他无用线程竞争锁来造成性能浪费,第二个if判断能拦截除第一个获得对象锁线程以外的线程。

    如果不加第二次判空,我们考虑下线程A,线程B都阻塞在了获取锁的步骤上,其中A获得锁—实例化了对象—-释放锁,之后B—获得锁—实例化对象,此时违反了我们单例模式的初衷。

    问题

    双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。

    双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因。

    singleton = new Singleton();

    该语句非原子操作,实际是三个步骤。

    1.给 Singleton 分配内存;

    2.调用 Singleton 的构造函数来初始化成员变量;

    3.将给 singleton 对象指向分配的内存空间(此时 singleton 才不为 null );

    虚拟机的指令重排序–>

    执行命令时虚拟机可能会对以上3个步骤交换位置 最后可能是132这种 分配内存并修改指针后未初始化 多线程获取时可能会出现问题。

    当线程A进入同步方法执行singleton = new Singleton();代码时,恰好这三个步骤重排序后为1 3 2,

    那么步骤3执行后 singleton 已经不为 null ,但是未执行步骤2,singleton对象初始化不完全,此时线程B执行 getInstance() 方法,第一步判断时 singleton 不为null,则直接将未完全初始化的singleton对象返回了。

    解决

    如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的,同时还会禁止指令重排序

    所以使用volatile关键字会禁止指令重排序,可以避免这种问题。使用volatile关键字后使得 singleton = new Singleton();语句一定会按照上面拆分的步骤123来执行。

    另一个问题

    单例模式并不是绝对安全的,可以通过反射来破坏,只有枚举安全类是安全的。

    部分内容参考链接:https://blog.csdn.net/qq646040754/article/details/81327933

    java设计模式

    阅读 18发布于 今天 03:26

    赞收藏

    分享

    本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

    114551.html

    文若杂谈

    关注专栏

    114551.html

    文若杂谈

    1声望

    0粉丝

    关注作者

    0 条评论

    得票时间

    114551.html

    提交评论

    114551.html

    文若杂谈

    1声望

    0粉丝

    关注作者

    宣传栏

    目录

    双重校验锁代码

    public class DoubleLock {

    private static DoubleLock doubleLock;

    private DoubleLock(){

    }

    public static DoubleLock getInstance(){

    if (doubleLock == null){

    synchronized (DoubleLock.class){

    if (doubleLock == null){

    doubleLock = new DoubleLock();

    }

    }

    }

    return doubleLock;

    }

    }

    优点

    安全且在多线程情况下能保持高性能,第一个if判断避免了其他无用线程竞争锁来造成性能浪费,第二个if判断能拦截除第一个获得对象锁线程以外的线程。

    如果不加第二次判空,我们考虑下线程A,线程B都阻塞在了获取锁的步骤上,其中A获得锁—实例化了对象—-释放锁,之后B—获得锁—实例化对象,此时违反了我们单例模式的初衷。

    问题

    双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。

    双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因。

    singleton = new Singleton();

    该语句非原子操作,实际是三个步骤。

    1.给 Singleton 分配内存;

    2.调用 Singleton 的构造函数来初始化成员变量;

    3.将给 singleton 对象指向分配的内存空间(此时 singleton 才不为 null );

    虚拟机的指令重排序–>

    执行命令时虚拟机可能会对以上3个步骤交换位置 最后可能是132这种 分配内存并修改指针后未初始化 多线程获取时可能会出现问题。

    当线程A进入同步方法执行singleton = new Singleton();代码时,恰好这三个步骤重排序后为1 3 2,

    那么步骤3执行后 singleton 已经不为 null ,但是未执行步骤2,singleton对象初始化不完全,此时线程B执行 getInstance() 方法,第一步判断时 singleton 不为null,则直接将未完全初始化的singleton对象返回了。

    解决

    如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的,同时还会禁止指令重排序

    所以使用volatile关键字会禁止指令重排序,可以避免这种问题。使用volatile关键字后使得 singleton = new Singleton();语句一定会按照上面拆分的步骤123来执行。

    另一个问题

    单例模式并不是绝对安全的,可以通过反射来破坏,只有枚举安全类是安全的。

    部分内容参考链接:https://blog.csdn.net/qq646040754/article/details/81327933

    展开全文
  • 开始复习设计模式,一开始理解单例模式中的双重校验锁卡住了,想通了后就自己做了段思维导图来帮助自己理解。其实理解下来并不难,但还是记录下来帮助自己回忆和借机试试养成写博客的习惯~public class Singleton {...

    开始复习设计模式,一开始理解单例模式中的双重校验锁卡住了,想通了后就自己做了段思维导图来帮助自己理解。

    其实理解下来并不难,但还是记录下来帮助自己回忆和借机试试养成写博客的习惯~

    public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {

    }

    public static Singleton getUniqueInstance() {

    if (uniqueInstance == null) {

    synchronized (Singleton.class) {

    if (uniqueInstance == null) {

    uniqueInstance = new Singleton();

    }

    }

    }

    return uniqueInstance;

    }

    }

    这是懒汉模式下双重校验锁下的简单代码

    public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {

    }

    public static Singleton getUniqueInstance() {

    if (uniqueInstance == null) {

    synchronized (Singleton.class) {

    uniqueInstance = new Singleton();

    }

    }

    return uniqueInstance;

    }

    }

    懒汉模式下非双重校验锁下的简单代码

    差别在于第二个if判断能拦截第一个获得对象锁线程以外的线程。

    笔者顺便做了张思维导图截图,模板可能选得不好,还是要多练练哈。

    9f99b2bbe3c668072ca3bf1c65c39e8a.png

    展开全文
  • 双检锁/双重校验锁(DCL,即 double-checked locking)JDK 版本:JDK1.5 起是否 Lazy 初始化:是是否多线程安全:是实现难度:较复杂描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的...

    双检锁/双重校验锁(DCL,即 double-checked locking)

    JDK 版本:JDK1.5 起

    是否 Lazy 初始化:是

    是否多线程安全:是

    实现难度:较复杂

    描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。

    实例

    public class Singleton {

    private volatile static Singleton singleton;

    private Singleton (){}

    public static Singleton getSingleton() {

    if (singleton == null) {

    synchronized (Singleton.class) {

    if (singleton == null) {

    singleton = new Singleton();

    }

    }

    }

    return singleton;

    }

    }

    我们可以看到第二行中有个volatile 的关键字,那么volatile 有啥用呢?这时候我们要先了解在java中通过new的方式创建对象的过程的具体步骤

    e913eecce3b40ea4884d288155787b80.png

    在通过new创建对象时有三个步骤:

    分配内存空间

    初始化对象

    将内存空间的地址赋值给对应的引用

    但是这个操作是非原子性操作,啥意思?就是说它不是一个整体操作,它的执行顺序是能被打乱的

    可能就成了这样:

    ddb8e4daafa0a56e1b1822cbc269c3a9.png

    为什么会出现这种情况呢?是因为JVM在加载当前操作的时候是由指令来执行这些代码的,也就是这些代码会被翻译成一个一个的指令刚才的三步就是三个指令。指令执行要有顺序,但是jvm执行默认的时候是没有顺序的。但是这个乱序概率发生的概率非常非常的低,正常情况在是不会发生的但是要是在运行过程中电脑卡机了就可能发生cup被抢导致了正在执行的指令被抢。所以我们有个指令重排的概念,什么是指令重排?简单点刚才我们指令顺序混乱就是指令重排,而volatile的作用就是能禁止指令重排,也就是会让jvm必须按123的顺序执行。

    bae7b3d588e25d54fb2048873821fe46.png

    所以不加volatile 正常情况下不会发生问题,但是仍有极小概率会出事,保险起见还是加了吧。

    当然,我讲的比较浅,个人又比较菜,还在学习中,我觉得我还可以抢救一下,有错的地方多多包容一下,请帮我指正,想再深入的小伙伴可以搜索下volatile的原理

    展开全文
  • 最近看一些Java内存模型方面的书,讲了一下Java的对象的内存分配过程,其中有个例子讲解多线程锁的问题,说了下面的例子:单例写法 双重校验写法//------------------------双重校验锁------------------private ...

    最近看一些Java内存模型方面的书,讲了一下Java的对象的内存分配过程,其中有个例子讲解多线程锁的问题,说了下面的例子:

    单例写法 双重校验写法

    //------------------------双重校验锁------------------

    private static Singleton singleton2;//-------1

    public static Singleton getInstance4() {//------------2

    if (singleton2 == null) {//-----------------------3

    synchronized (Singleton.class) {//------------4

    if (singleton2 == null)//-----------------5

    singleton2 = new Singleton();//-------6

    }

    }

    return singleton;

    }

    问题处在了第6步,Java创建对象的第6步可以分为以下三步:

    memory = allocate();//----1

    ctorInstance(memory);//-2

    instance = memory;//-----3

    其中2,3步在JVM编译优化时可能发生重排序,这和采用的JIT有关,并且该重排序遵循intra-thread semantics法则(重排序后不会影响单线程的执行结果)。

    如果发生重排序,第3步先于第2步执行,那么A线程可能只是让对象指向内存地址,并没有实质的初始化对象,那么线程B调用时就会发生错误。

    解决方案

    采用volatile

    在Java1.5以后,volatile关键字被加强,这种重排序不允许在多线程中发生。

    即在对象声明加上volatile关键字。

    实质:禁止编译的重排序。

    采用JVM初始化类时加锁

    JVM的类的初始化阶段,会获取锁,该锁可以同步多线程对一个类的初始化。

    此时衍生一种称为:Initialization On Demand Holder idiom的解决方案。

    //-----------------------------静态内部类---------------

    private static class SingletonHolder {

    private static final Singleton INSTANCE = new Singleton();

    }

    public static Singleton getInstance3() {

    return SingletonHolder.INSTANCE;

    }

    9cc07280c69c

    JVM在多线程中初始化对象过程.jpg

    ** 实质: **利用JVM的多线程初始化对象的特性,允许重排序,但对其他线程不可见。

    另外根据Java语言规范,一个类在一下5种情况会发生初始化:

    T是一个类,并且T的实例被创建。

    T是一个类,且T中的静态方法被调用

    T是一个类,且T中的一个静态字段被赋值。

    T是一个类,且T中的非常量字段被使用

    T是一个顶级类(TOP Level Class),有断言语句嵌套在T内部被执行。(assert语句,很少用改规则)

    展开全文
  • 双重校验锁实现单例模式,以及面试时常见问题。
  • 双重校验锁实现对象单例 package com.heu.wsq.basic; /** * 双重校验所锁实现对象单例(线程安全) * @author wsq * @date 2021/1/24 */ public class Singleton { /** * volatile作用: * 1.保证内存可见性...
  • 单例模式双重校验锁

    2021-03-20 15:19:20
    * 单例模式双重校验锁 * * */ public class SingleModel { //创建 SingleModel 的一个对象 private static volatile SingleModel instance; // 让构造函数为 private,这样该类就不会被实例化 private ...
  • 单例设计双重校验锁这种方式采用双锁机制,安全且在多线程情况下能保持高性能。但其中也有优缺点双重校验锁代码public class DoubleLock {private static DoubleLock doubleLock;private DoubleLock(){}public ...
  • 双重校验锁单例实现

    2019-03-15 16:03:54
    一个双重校验锁实现的单例示例。 简单实现 public class Singleton { private Singleton() { }; private static volatile Singleton singleton = null; public static Singleton getInstance() { if ...
  • 针对懒汉模式,为了解决线程安全引入双重校验锁机制,代码示例如下: public class SignleObject { private static SignleObject instance = null; private SignleObject() { } public static SignleObject ...
  • 单例设计双重校验锁这种方式采用双锁机制,安全且在多线程情况下能保持高性能。但其中也有优缺点 双重校验锁代码 public class DoubleLock { private static DoubleLock doubleLock; private DoubleLock(){ ...
  • *双重校验锁方式实现单例模式 */ public class Singleton { //静态实例变量 private volatile static Singleton uniqueInstance; // 私有化构造函数 private Singleton() { } // 静态public方法,向整个...
  • 双重校验锁实现单例对象 第一个判断if (uniqueInstance == null), 可以提高效率,如果直接在方法上加锁,那么线程得到已有的单例对象也会阻塞别的线程。如果单例对象不为空,直接返回就好了,不用加锁。 第二个判断...
  • 那么传说中双重判断来了,在加锁前判断一次INSTANCE是否等于null,不等于直接就返回实例了,这样也不用再加判断了。(也就是首次访问需要同步,而之后就没有synchronized了),这样做还有问题吗? biao急啊? go on....
  • /** ... * @Desc 懒汉式 双重校验锁 */ public class SingletonTest { //变量私有 private volatile static SingletonTest instance = null;//用volatile static 修饰 //构造方法私有 priva.
  • //双重校验锁实现单例 public class synchronizedDemo { //volatile 禁止jvm重排 保证内存的可见性 private volatile static synchronizedDemo uniqueIntance; //private 构造函数私有化 private synchronizedDemo()...
  • 单例模式-双重校验锁

    2018-06-20 11:54:00
    /** * 单例模式-双重校验锁 * @author szekinwin * */public class SingleTon3 { private SingleTon3(){}; //私有化构造方法 private static volatile SingleTon3 singleTon=null; pu...
  • 为什么要用双重校验锁实现单例模式? 单例实现有饿汉模式与懒汉模式,懒汉模式能够延迟加载,使用较多,但是懒汉模式在多线程下会出现问题,即有可能会产生多个实例。 下面是懒汉模式实现的单例模式的代码 public ...
  • 双检锁/双重校验锁(DCL,即 double-checked locking) JDK 版本 JDK1.5 起 是否 Lazy 初始化 是 是否多线程安全 是 实现难度 较复杂 描述:这种方式采用双锁机制,安全且在多线程情况下能保持高...
  • * 解决双重校验锁的线程安全问题 * 当前代码有安全问题,有三种解决方法 * 1)在方法签名上添加synchronized, 使方法变为同步方法 * 2)在存在线程安全问题的成员变量声明前添加volatile * 3)若在线程安全问题的...
  • 本文主要讲述了Java单例模式之双重校验锁中volatile关键字的作用。 更多文章欢迎访问我的个人博客–>幻境云图 上篇文章Java设计模式(一)–单例模式中讲了Java单例模式的几种写法,其中懒汉式和双重...
  • 本文主要讲如何用双重校验锁实现单例模式懒加载 下面就是网上读烂了的一个代码段 private volatile static SingletonClass instance = null; public static SingletonClass getInstance() { if (instance == ...
  • 为什么是双重校验锁实现单例模式呢? public class Singleton { // volatile关键字可以防止jvm指令重排优化 // 因为 instance = new Singleton() 这句话可以分为三步: // 1. 为 instance 分配内存空间; // 2. ...
  • 双重校验锁实现单例模式: public class Singleton { //采用volatile修饰 private volatile static Singleton singleton; //构造方法私有化 private Singleton(){} //双重校验锁 public static Singleton...
  • 我们来解读一下,双重校验锁的意义何在,为什么要这样设计。 首先,第一次校验,也就是第一个判断if(singleton == null),意义是由于单利模式只需创建一个实列,所以当第一次创建实列成功之后,再次调用Singleton....
  • //双重校验锁实现对象单例(线程安全) public class Singleton { private volatile static Singleton uniqueInstance; private Singleton(){ } public static Singleton getUniqueInstance(){ //先判断对象...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 828
精华内容 331
关键字:

双重校验锁