volatile vs synchronized

今天来聊一聊Java并发编程中两个经常使用的关键字:volatile和synchronized。在介绍这两个关键字以前,首先要搞明白并发编程中的两个问题:java

  1. 线程之间是如何通讯的程序员

  2. 线程之间如何同步编程


Java内存模型

Java线程的通讯由Java内存模型(JMM)控制,Java内存模型的抽象如图:微信

Java线程之间的通讯老是隐式进行,通讯过程对程序员彻底透明。多个线程经过读-写共享内存来实现通讯。网络

图中线程A与线程B通讯的具体步骤是:多线程

  1. 线程A把更新过的共享变量刷新到主内存中并发

  2. 线程B从主内存读取共享变量ide

例如,共享变量x的初始值为0,线程A将x修改成1(x=x+1),线程B读取到的x就是1,对于程序员来说,就是线程A给线程B发消息说它把x的值更新为1。性能



第一个问题搞明白了,再思考一下第二个问题。线程之间如何同步?在并发编程中,有三个重要的概念:原子性、可见性、一致性。atom

原子性

在Java中,对基本数据类型的读取和赋值操做都属于原子操做。

x = 10;
x = x + 1;

上面两条语句中,第一句是原子操做,而第二句不是,为何呢?实际上,第二句代码被编译为3条指令:

  • 从内存中取x的值

  • x+1操做

  • 计算结果存入内存


可见性

当多个线程访问同一变量时,若是有一个线程修改了这个变量,那么其余线程马上能够看到修改后的值。


有序性

CPU执行指令是按照前后顺序执行的,可是指令的顺序并不必定等同于代码的顺序,编译器编译过程当中,为了提升性能,经常进行指令重排序。这种重排序不会改变单线程的语义,也就是说,你写的一段代码若是是单线程执行,编译器可能对执行进行重排序,但不论如何排序,最后获得的结果都是相同的。


另外,若是存在数据依赖性,编译器不会改变依赖关系的执行顺序。数据依赖性是指两个操做访问同一个变量,其中一个是写操做,那么这两个操做就有数据依赖性。


重排序对应多线程有哪些影响呢,咱们经过一段代码来看一下:

class ReorderExample {
   int a = 0;
   boolean flag = false;
   public void writer() {
       a = 1; // 1
       flag = true; // 2
   }
   Public void reader() {
       if (flag) { // 3
       int i = a * a; // 4
       ……
       }
   }
}

上述代码中,flag是变量a被初始化的标识,若是此时有两个线程A和B,A执行writer()方法,B执行reader()方法。因为1和二、3和4不存在数据依赖性,那么就有可能出现这种状况:

  • A先执行语句2

  • B执行了语句3和4

  • A执行语句1

最终的结果并非咱们想要的,此时,重排序破坏了语义。


线程同步

对于上面所说的线程同步问题如何避免呢?可使用Java中的volatile和synchronized这两个关键字。


volatile

volatile关键字比较轻量级,只能够修饰变量。volatile修饰的变量,若是值被更新,会当即刷新主内存,而读volatile修饰的变量时,JMM会把线程对应的本地内存置为无效,从主内存中读取。这样volatile就能够保证线程的可见性。


volatile关键字在必定程度上能够保证有序性:

  • 当第二个操做是volatile写时,不能进行重排序

  • 当第一个操做是volatile读时,不能进行重排序

  • 当第一个操做是volatile写,第二个操做是volatile读时,不能重排序

为了实现这些语义,JMM采用屏障插入策略:

  • 在volatile写操做前插入StoreStore屏障,后面插入StoreLoad屏障

  • 在volatile读操做后面插入LoadLoad屏障和LoadStore屏障

也就是说,volatile写操做前的全部写操做都必须执行完,且须要等到volatile写操做执行后才能执行读操做。volatile读操做执行完以后才能够进行其余操做。

能够把volatile当作一个屏障,其前面的操做不能放到volatile操做后面,后面的操做也不能放到volatile操做前面。


synchronized

synchronized比较重量级,能够用来修饰方法。synchronized关键字是给修饰对象加锁,只有得到锁的线程才能够执行,执行完后释放锁。所以synchronized保证了原子性和可见性。



文中图片来源于网络


本文分享自微信公众号 - 代码洁癖患者(Jackeyzhe2018)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索