既然synchronized是"万能"的,为什么还需要volatile呢?
synchronized
和volatile
两个关键字是Java并发编程中经常用到的两个关键字。我们知道synchronized
可以保证并发编程中不会出现原子性、可见性和有序性问题,而volatile只能保证可见性和有序性,那么,既然有了synchronized
,为什么还需要volatile
?
Java中为了解决多线程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,例:synchronized、volatile、final、concurren包等。
synchronized
通过加锁的方式,使得其在需要原子性、可见性和有序性这三种特性的时候都可以作为其中一种解决方案,看起来是“万能”的。的确,大部分并发控制操作都能使用synchronized
来完成。volatile
通过在volatile
变量的操作前后插入在JVM虚拟机内存屏障(JMM)的方式,保证了变量在并发场景下的可见性和有序性(操作系统的实现机制,汇编指令lock addl $0x0,(%rsp)
以及缓存一致性协议(MESI))。volatile
关键字是无法保证原子性的,而synchronized
通过jvm层指令monitorenter
和monitorexit
两个指令,可以保证被synchronized
修饰的代码在同一时间只能被一个线程访问,即可保证不会出现CPU时间片在多个线程间切换,即可保证原子性。synchronized
实现的锁本质上是一种阻塞锁,也就是说多个线程要排队访问同一个共享对象。而volatile
是Java虚拟机提供的一种轻量级同步机制,他是基于内存屏障实现的。说到底,他并不是锁,所以他不会有synchronized
带来的阻塞和性能损耗的问题。
- 在一般的程序中只要加上了synchronized就不需要再加上
volatile
,但是在单例模式则需要。这是因为为了防止指令重排。
注意:
- 本文中强调了JVM层面指令与操作系统层面的指令,请读者不要混淆。
在操作系统中带有lock前缀的的指令在多核处理器会发生两件事:
2.1 将当前处理器缓存行的数据写回到系统内存里面去
2.2 这个写回内存的操作会使其他CPU缓存行的数据无效
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: