Java的synchronized 能防止指令重排序吗?

Wesley13
• 阅读 697

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

Java的synchronized 能防止指令重排序吗?

扫码关注添加客服

进Python社群▲

Java的synchronized 能防止指令重排序吗?

扫码关注添加客服

*进Java社群*

作者丨码农二胖

来源丨java金融(ID:java4299)

引言

「二狗」:二胖你昨天请假了是不是又去面试了啊?
「二胖」:别说了我就出去试试水,看看现在工作好不好找,顺带出去找找打击,然后才能好好静下心来好好学习。
「二狗:」 那被打击的怎么样啊?知道自己是什么样的水平了吧,坏笑。
「二胖」:基础太差,一面就让回去等通知了,我要好好学习了,不跟你瞎扯了。
「二狗:」 都问了你什么问题啊,把你打击成这样?一起复盘下让我也好好准备下啊。
「二胖」:好吧,你既然这么好奇,那我就大概说下吧,你搬上小板凳仔细挺好了哦。我要开始我的表演了。
下面二胖第一面开始了。
「面试官」:二胖是吧,先做个自我介绍吧。
「二胖」:好的,我叫二胖,我来自长沙,今年25岁,从事java开发快3年了,现在在XX公司XX事业部担任高级「java」开发工程师,主要负责XX系统。。。。。
「面试官」:好的,我看你简历上写着熟练掌握并发编程你能跟我说说并发编程里面你都知道哪些关键字。
「二胖:」 这不就是要考我 synchronizedvolatile 这个我擅长啊,我特意背过的,synchronizedjava提供的一个关键字它主要能保证原子性、有序性它的底层主要是通过Monitor来实现的。volatile也是java的一个关键字它的主要作用是可以保证可见性。。。。此处省略1000字。
「面试官」:八股文背的不错,说了这么多,我们来动手试试吧,写一个双重校验锁(dcl)的单例我看看。
「二胖:」 从屁股口袋里拿出了笔三下五除二就把它默写出来了。
「面试官」:你有说道volatile关键字和synchronized关键字。synchronized可以保证原子性、有序性和可见性。而volatile却只能保证有序性和可见性。那么,我们再来看一下双重校验锁实现的单例,已经使用了synchronized,为什么还需要volatile?这个volatile是否可以去掉?
「二胖:」 让我想想,貌似好像确实可以去掉。
「面试官:」 我们今天的面试就到这里吧,后续有消息人事会联系你,感谢你今天来面试。

二胖很郁闷回去谷歌了下这个问题,「stackoverflow」上也有这个问题,看样子不只我一个人不知道这个问题吗?看样子面试挂的不冤「以上故事纯属虚构,如有雷同请以本文为主。」

synchronized 的有序性?

我们先来看看没有加volatile 修饰的单例:

 1   public class Singleton {  
 2      private static Singleton singleton;  
 3       private Singleton (){}  
 4       public static Singleton getSingleton() {  
 5       if (singleton == null) {  
 6           synchronized (Singleton.class) {  
 7               if (singleton == null) {  
 8                   singleton = new Singleton();  
 9               }  
 10           }  
 11       }  
 12       return singleton;  
 13       }  
 14   }  

上述代码看下来是不是感觉没啥问题。首先我们先来看下这一行代码到底干了哪些事情

singleton = new Singleton() 

Java的synchronized 能防止指令重排序吗? 上述过程我们可以简化成3个步骤:

「JVM」为对象分配一块内存M。

②在内存M上为对象进行初始化。

③将内存M的地址复制给singleton变量。 

这个步骤有两种执行顺序可以按照 「①②③」或者「①③②」来执行。当我们按照「①③②」的顺序来执行的时候 我们假设有两个线程ThreadAThreadB 同时来请求Singleton.getSingleton方法:

  • 正常情况按照 「①②③」的顺序来执行

    「第一步:」ThreadA 进入到第8行,执行 singleton = new Singleton() 进行对象的初始化(按照对象初始化的过程 「①②③」)执行完。

    「第二步:」 ThreadB进入第5行判断singleton不为空(第一步已经初始化好了),直接返回singleton**第三步:**拿到这个对象做其他的操作。这样看下来是不是没有啥问题。

  • 那如果对象初始化的时候按照 「①③②」 的步骤我们再来看看:「第一步:」 ThreadA进入到第8行,执行 singleton = new Singleton() 执行完.①JVM为对象分配一块内存M。③将内存的地址复制给singleton变量。

    「第二步:」  此时ThreadB直接进入第5行,发现singleton已经不为空了然后直接就跳转到12行拿到这个singleton返回去执行操作去了。此时ThreadB拿到的singleton对象是个半成品对象,因为还没有为这个对象进行初始化(「②还没执行」)。「第三步:」 所以ThreadB拿到的对象去执行方法可能会有异常产生。至于为什么会这样列?《Java 并发编程实战》有提到

有 synchronized 无 volatile 的 DCL(双重检查锁) 会出现的情况:线程可能看到引用的当前值,但对象的状态值确少失效的,这意味着线程可以看到对象处于无效或错误的状态。

说白了也就是ThreadB是可以拿到一个引用已经有了但是内存资源还没有分配的对象。如果要解决创建对象按照①②③的顺序,其实也就是为了解决指令重排只要第2行加个volatile修饰就好。

「说好的synchronized 不是可以保证有序性的吗?volatile的有序性?synchronized 不能不够保证指令重排吗?」

怎么来定义顺序呢?《深入理解Java虚拟机第三版》有提到

Java程序中天然的有序性可以总结为一句话:如果在本线程内观察,所有操作都是天然有序的。如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内似表现为串行的语义”,后半句是指“指令重排”现象和“工作内存与主内存同步延迟”现象。

  • 「synchronized」 的有序性是持有相同锁的两个同步块只能串行的进入,即被加锁的内容要按照顺序被多个线程执行,但是其内部的同步代码还是会发生重排序,使块与块之间有序可见。

  • 「volatile」的有序性是通过插入内存屏障来保证指令按照顺序执行。不会存在后面的指令跑到前面的指令之前来执行。是保证编译器优化的时候不会让指令乱序。

  • 「synchronized 是不能保证指令重排的」

站在巨人的肩膀上摘苹果: https://stackoverflow.com/questions/7855700/why-is-volatile-used-in-double-checked-locking https://juejin.cn/post/6844903920599302152                       

Java的synchronized 能防止指令重排序吗?

程序员专栏 扫码关注填加客服 长按识别下方二维码进群



近期精彩内容推荐:   员工因上厕所时间超长被开除了 程序员连续15天加班到凌晨2点在餐厅泪崩! 还在try...catch?如果是那你就out了! Python很慢?Python之父一句话亮了



在看点这里好文分享给更多人↓↓
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
GitHub 上 25 个 Python 学习资源,墙裂推荐!
Python实战社群Java实战社群长按识别下方二维码,按需求添加!(https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9nU2dLV0Z3TjFkQ0xpYzNJeDZEc1JyZlh3aWFndExYTDl
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Python 之父为什么嫌弃 lambda 匿名函数?
Python实战社群Java实战社群长按识别下方二维码,按需求添加!(https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9nU2dLV0Z3TjFkQ0xpYzNJeDZEc1JyZlh3aWFndExYTDl
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这