Java并发编程 02 volatile 关键字

来源:互联网 发布:logic studio mac 编辑:程序博客网 时间:2024/06/04 01:04

volatile的特性

重排序

重排序是计算机为了提高程序执行效率而对代码的执行顺序进行调整。个人感觉就是乱序执行。若两行指令之间没有依赖关系,那么计算机可以对他们的顺序进行重排序,但若两行之间的某个变量被volatile修饰后,重排序规则会发生变化。

在以下情况下,即使两行代码之间没有依赖关系,也不会发生重排序:

volatile读若volatile读操作的前一行为volatile读/写,则这两行不会发生重排序;volatile读操作和它后一行代码都不会发生重排序volatilevolatile写操作和它前一行代码都不会发生重排序;若volatile写操作的后一行代码为volatile读/写,则这两行不会发生重排序。

可见性

“内存可见性”指的是一条线程修改完一个共享变量后,另一个线程若访问这个变量将会访问到修改后的值。即:一条线程对共享变量的修改,对其他线程立即可见。但如果未对共享变量采用同步机制,那么共享变量的修改不会对其他线程立即可见。

通过上文可知,在Java中每条线程都有各自独立的存储空间,此外还有一个所有线程共享的内存空间。 当开启线程时,系统会将共享内存中的所有共享变量拷贝一份到线程专属的存储空间中。接下来该线程在结束前的所有操作都是基于自己的存储空间进行的。因此,若一条线程改变了一个共享变量,仅仅改变的是这条线程专属存储空间中的变量值;此时若其他线程访问这个变量,访问的仍然是先前从共享存储空间读出来的值。 然而我们希望一条线程将某个共享变量修改后,其他线程能立即访问到这个最新的值,而不是失效值。
这时就需要同步机制来解决这个问题。

如何确保共享变量的可见性?要确保所有共享变量对所有线程是可见的,就需要给所有共享变量使用同步。在Java中你可以选择将共享变量用同步代码块包裹或用volatile修饰共享变量。volatile修饰了一个成员变量后,这个变量的读写就会比普通变量多一些步骤。

volatile变量写 当被volatile修饰的变量进行写操作时,这个变量将会被直接写入共享内存,而非线程的专属存储空间。volatile变量读 当读取一个被volatile修饰的变量时,会直接从共享内存中读,而非线程专属的存储空间中读。

通过对volatile变量读写的限制,就能保证线程每次读到的都是最新的值,从而确保了该变量的内存可见性。

volatile变量赠送的附加功能

进行volatile写操作时,不仅会将volatile变量写入共享内存,系统还会将当前线程专属空间中的所有共享变量写入共享内存。 进行volatile读操作时,系统也会一次性将共享内存中所有共享变量读入线程专属空间。 这就意味着,如果普通变量在volatile写操作之前被修改,那么在volatile读操作之后就能正确读到他们。 但是,在volatile写操作之后被修改的普通变量 和 在volatile读操作之前被访问的普通变量 都不具有内存可见性。

原子性

原子性指的是一组操作必须一起完成,中途不能被中断。volatile能确保long、double读写的原子性

在Java中的所有类型中,有long、double类型比较特殊,他们占据8字节(64比特),其余类型都小于64比特。在32位操作系统中,CPU一次只能读取/写入32位的数据,因此对于64位的long、double变量的读写会进行两步。在多线程中,若一条线程只写入了long型变量的前32位,紧接着另一条线程读取了这个只有“一半”的变量,从而就读到了一个错误的数据。
为了避免这种情况,需要在用volatile修饰long、double型变量。

在内存可见性与原子性上,volatile就相当于是同步的setter和getter函数。但并不具有volatile的重排序规则,同步块只确保同步块内部的指令不发生重排序,并不确保同步块以外的指令的重排序。

参考链接:
Java并发编程:volatile关键字解析

原创粉丝点击