单例模式 指令重排_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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 虚拟机配置DNS服务器「建议收藏」

    虚拟机配置DNS服务器「建议收藏」虚拟机配置DNS服务DNS简介下面演示如何通过虚拟机安装DNS服务器windows2003(DNS服务端)安装DNS服务客户端做相应配置回到windows2003新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入DNS简介

    2022年5月6日
    363
  • centos systemctl_正在不使用中

    centos systemctl_正在不使用中CentOS7.x开始,CentOS开始使用systemd服务来代替daemon,原来管理系统启动和管理系统服务的相关命令全部由systemctl命令来代替。1、原来的service命令与systemctl命令对比daemon命令systemctl命令说明service[服务]startsystemctlstart[unit…

    2025年12月14日
    2
  • 奇怪的电梯

    奇怪的电梯奇怪的电梯【问题描述】某栋大楼有一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1≤i<N)上有一个数字K(≤K≤N)电梯只有四个按钮:开、关、上、下。上、下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:33125代表了Ki(K1=3,K2=3,…),从一层开始。在一层按“上”可以到4层,按“下”是不起作用的,因为没有-2层。那么从A层到B层至少要按几次按钮呢?【输入格式】第1行为3个用1个空格隔开的正整数,表示N、A、B(l≤N≤200,1≤

    2022年6月14日
    39
  • 那些惊艳的算法们(四)——唯一ID生成器snowflake

    那些惊艳的算法们(四)——唯一ID生成器snowflake同步发表在个人博客中:http://blog.lanjingdejia.com/articles/2019/01/15/1547516544183.html分布式全局唯一ID生成器很多场景需要使用全局唯一ID,用来标识唯一一条消息,唯一一笔交易,唯一一个用户,唯一一张图片等等。传统数据库表的自增主键是很简单的一种实现方式,前提是你没有分库,也没有分表,如果你分表了,id就会重复,失去唯一性…

    2022年6月22日
    101
  • Centos 6安装完美搭建mysql、php、apache之旅

    Centos 6安装完美搭建mysql、php、apache之旅

    2022年1月20日
    42
  • kernel logo到开机动画之间闪现黑屏(android 5.X)

    kernel logo到开机动画之间闪现黑屏(android 5.X)

    2022年2月6日
    56

发表回复

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

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