文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-03 16:12

关注

 引言

「二狗」:二胖你昨天请假了是不是又去面试了啊?

「二胖」:别说了我就出去试试水,看看现在工作好不好找,顺带出去找找打击,然后才能好好静下心来好好学习。

「二狗:」 那被打击的怎么样啊?知道自己是什么样的水平了吧,坏笑。

「二胖」:基础太差,一面就让回去等通知了,我要好好学习了,不跟你瞎扯了。

「二狗:」 都问了你什么问题啊,把你打击成这样?一起复盘下让我也好好准备下啊。

「二胖」:好吧,你既然这么好奇,那我就大概说下吧,你搬上小板凳仔细挺好了哦。我要开始我的表演了。

下面二胖第一面开始了。

「面试官」:二胖是吧,先做个自我介绍吧。

「二胖」:好的,我叫二胖,我来自长沙,今年25岁,从事java开发快3年了,现在在XX公司XX事业部担任高级「java」开发工程师,主要负责XX系统。。。。。

「面试官」:好的,我看你简历上写着熟练掌握并发编程你能跟我说说并发编程里面你都知道哪些关键字。

「二胖:」 这不就是要考我 synchronized 和volatile 这个我擅长啊,我特意背过的,synchronized 是java提供的一个关键字它主要能保证原子性、有序性它的底层主要是通过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.   }   

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

  1. singleton = new Singleton()  

上述过程我们可以简化成3个步骤:

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

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

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

这个步骤有两种执行顺序可以按照 「①②③」或者「①③②」来执行。当我们按照「①③②」的顺序来执行的时候 我们假设有两个线程ThreadA 和ThreadB 同时来请求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程序中天然的有序性可以总结为一句话:如果在本线程内观察,所有操作都是天然有序的。如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内似表现为串行的语义”,后半句是指“指令重排”现象和“工作内存与主内存同步延迟”现象。

结束

由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。

本文转载自微信公众号「 java金融」,可以通过以下二维码关注。转载本文请联系 java金融公众号。

 

来源: java金融内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯