单例模式 指令重排_php单例模式

单例模式 指令重排_php单例模式单例模式写法有很多,于是我看到了这么一种写法:publicclassSingletonTest{privateSingletonTest(){}privatestaticSingletonTestsingletonTest=null;publicstaticSingletonTestgetSingletonTest()…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

单例模式写法有很多,于是我看到了这么一种写法:

public class SingletonTest {

    private SingletonTest() {
    }

    private static SingletonTest singletonTest = null;

    public static SingletonTest getSingletonTest() {
        if (singletonTest == null) {
            // 若singletonTest为空,则加锁,再进一步判空
            synchronized (SingletonTest.class) {
                // 再判断一次是否为null
                if (singletonTest == null) {
                    //若为空,则创建一个新的实例
                    singletonTest = new SingletonTest();
                }
            }
        }
        return SingletonTest;
    }
}

这种写法算是一个考虑比较得当的设计了 为了防止多线程调用产生多个实例,采用了同步锁 加锁位置得当,尽可能降低了加锁对性能的影响 
但是在这个示例下方,有指出可能会由于指令重排的影响,导致代码执行错误,只是概率很低。

 

我不由得重新审视着这段代码,难道看似稳的一逼的代码如此不堪一击? 
于是,我大致了解了下指令重排: 
指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。 
也就是说,JVM为了执行效率会将指令进行重新排序,但是这种重新排序不会对单线程程序产生影响。

首先,JVM是如何保证单线程下的指令在重新排序后执行结果不受影响的呢?

(此处省略一万字……关于happens-before,感兴趣的请看原文)

 由于singletonTest = new SingletonTest()操作并不是一个原子性指令,会被分为多个指令:

memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址

但是经过重排序后如下:

memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化
ctorInstance(memory); //2:初始化对象

 若有A线程进行完重排后的第二步,且未执行初始化对象。此时B线程来取singletonTest时,发现singletonTest不为空,于是便返回该值,但由于没有初始化完该对象,此时返回的对象是有问题的。这也就是为什么说看似稳的一逼的代码,实则不堪一击。 
另外,在《java并发编程实战》16.2.4中对该种双重检查加锁(DCL)提出了批评。批评的主要点在于,该方式会导致上述指出的取到一个无效或错误状态的对象。 
上述代码的改进方法:将singletonTest声明为volatile类型即可(volatile有内存屏障的功能)。

参考博客: https://www.cnblogs.com/senlinyang/p/7875458.html

 

 

节选自:https://blog.csdn.net/yuruixin_china/article/details/80550209

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/180992.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 小草 客户端 android,小草app安卓版入口

    小草 客户端 android,小草app安卓版入口这里小编为大家提供一个非常不错的线上的看剧软件哦,这里每天都会更新一些市面上比较火爆的各种视频资源,而且更新的速度也是十分的快捷的,无需任何的花费,的大家就可以轻松的在线来观看了。整个平台为大家提供的各种服务也是十分的优质的,线上还有专业的客服人员在线为大家提供更加优质的服务哦。小草app安卓版入口的特色:1、线上的所有的视频资源内容都是有平台专业的人士为大家进行审核的,保证都是正版的内容哦;2、…

    2022年6月15日
  • Android多媒体-人脸识别

    Android多媒体-人脸识别

    2021年11月23日
  • k8s的apiserver_k8s源码分析

    k8s的apiserver_k8s源码分析APIserverfunc main() { … #核心 command := app.NewAPIServerCommand() #日志 logs.InitLogs() defer logs.FlushLogs() …}#NewAPIServerCommand#核心是调用run函数 运行AIPserver 永远不会退出return Run(completedOptions, genericapiserver.SetupSignalHandler())#Run#创建

    2022年8月11日
  • 红队评估实战靶场(1)

    0x00前言[滑稽][滑稽]又是我,我又来发水文了,这几天打靶机打上瘾了,再来更新篇靶机的文章0x01靶机渗透配置好靶机后,这里需要打开win7,来到c盘目录下启动phpstudy启动完成后

    2021年12月11日
  • android之存储篇_SQLite数据库_让你彻底学会SQLite的使用「建议收藏」

    SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。但有一种情况例外:定义为INTEGERPRIMARYKEY的字段只能存储64位整数,当向这种字段保存除整数以外的数据时,将会产生错误。另外,SQLite在解析CR…

    2022年3月10日
  • 用浏览器怎样监控网页内容变化

    用浏览器怎样监控网页内容变化随着互联网的发展,一般单位或企业都通过网站对外发布动态消息;各种管理软件、saas系统也通过web页面实现订单管理、工单派遣等。如何在第一时间接收消息或工单提醒,就需要实时刷新监控页面内容变化。…

    2022年7月17日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号