Java synchronized 不能防止指令重排序

1、指令重排

     首先说一下什么是指令重排,指令重排是指JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序。指令重排的目的是为了在不改变程序执行结果的前提下,优化程序的运行效率。需要注意的是,这里所说的不改变执行结果,指的是不改变单线程下的程序执行结果,即必须遵守as-if-serial语义(as-if-serial语义:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变)

2、synchronized 的有序性

 Java 里的操作无序现象是什么?

          如果在一个线程观察另一个线程,所有操作都是无序的指的是 “指令重排序” 和 “工作内存与主内存同步延迟” 现象。

指令重排是JVM层面对程序进行的优化措施,如果不深入了解,则在并发编程时可能会发生难以发现的Bug。

截止JDK1.8, Java 里只有 volatile 变量是能实现禁止指令重排的。

synchronized 虽不能禁止指令重排,但能保证有序性

这个有序性是相对语义来看的,线程与线程间,每一个 synchronized 块可以看成是一个原子操作,它保证每个时刻只有一个线程执行同步代码,它可以解决上面引述的工作内存和主内存同步延迟现象引发的无序

synchronized 和 volatile 的有序性与可见性是两个角度来看的:

    synchronized 是因为块与块之间看起来是原子操作,块与块之间有序可见

    volatile 是在底层通过内存屏障防止指令重排的,变量前后之间的指令与指令之间有序可见

synchronized 和 volatile 有序性不同也是因为其实现原理不同:

    synchronized 靠操作系统内核互斥锁实现的,相当于 JMM 中的 lock 和 unlock。退出代码块时一定会刷新变量回主内存

    volatile 靠插入内存屏障指令防止其后面的指令跑到它前面去了

总而言之就是, synchronized 块里的非原子操作依旧可能发生指令重排

为何双重校验单例还要加上 volatile 的原因?

简单来讲,因为 new 这个指令是非原子操作,底层是分成几条指令来执行的,加上 volatile 是禁止指令重排,保证别的线程读到的时候一定是状态和引用正常的、一个完整的对象,防止其他线程看到的是对象还没有完全实例化的内容。


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值