面试题:Java的synchronized 能防止指令重排序吗?

简介: 面试题:Java的synchronized 能防止指令重排序吗?

引言

二狗:二胖你昨天请假了是不是又去面试了啊?
二胖:别说了我就出去试试水,看看现在工作好不好找,顺带出去找找打击,然后才能好好静下心来好好学习。
二狗: 那被打击的怎么样啊?知道自己是什么样的水平了吧,坏笑。
二胖:基础太差,一面就让回去等通知了,我要好好学习了,不跟你瞎扯了。
二狗: 都问了你什么问题啊,把你打击成这样?一起复盘下让我也好好准备下啊。
二胖:好吧,你既然这么好奇,那我就大概说下吧,你搬上小板凳仔细挺好了哦。我要开始我的表演了。
下面二胖第一面开始了。
面试官:二胖是吧,先做个自我介绍吧。
二胖:好的,我叫二胖,我来自长沙,今年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() 

来源网上
上述过程我们可以简化成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

目录
相关文章
|
10天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
11天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
33 3
|
2月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
2月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
2月前
|
存储 安全 Java
面试高频:Synchronized 原理,建议收藏备用 !
本文详解Synchronized原理,包括其作用、使用方式、底层实现及锁升级机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
面试高频:Synchronized 原理,建议收藏备用 !
|
2月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
76 4
|
2月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
49 4
|
3月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
45 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
3月前
|
算法 Java 程序员
Java中的Synchronized,你了解多少?
Java中的Synchronized,你了解多少?
|
3月前
|
Java
让星星⭐月亮告诉你,Java synchronized(*.class) synchronized 方法 synchronized(this)分析
本文通过Java代码示例,介绍了`synchronized`关键字在类和实例方法上的使用。总结了三种情况:1) 类级别的锁,多个实例对象在同一时刻只能有一个获取锁;2) 实例方法级别的锁,多个实例对象可以同时执行;3) 同一实例对象的多个线程,同一时刻只能有一个线程执行同步方法。
23 1

热门文章

最新文章