前言
在Java开发中,若仅为读写一个或两个对象就使用同步锁处理,会增大程序内存开销,而volatile关键字就可以很好地解决这个问题。声明一个对象为volatile,则Java编译器和虚拟机就会清楚该对象是可能被另一个线程并发更新。不过在了解volatile之前熟悉一下并发编程中的三个特性:原子性,可见性,有序性。
1.并发编程三个特性:原子性,可见性,有序性
-
原子性
对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。
x = 10; // 1
y = x; // 2
x++; // 3
x = x + 1; // 4
复制代码
以上1是原子性操作,其他3个语句均不是原子性操作。
2实际上包含2个操作,先要读取x的值,再将x的值写入内存,读取x的值以及 将x的值写入内存这2个操作都是原子性操作,但合起来就不是原子性操作啦。
x++和 x = x+1 包括3个操作:读取x的值,进行加1操作,写入新值。换句话说,只有简单的读取、赋值才是原子操作。
可见性
指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。有序性
Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
关键字volatile
被volatile修饰共享变量,有两种含义
- 保证了不同线程对这变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 禁止进行指令重排序。
volatile能否保证原子性?
自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的。
volatile能否保证有序性?
volatile关键字能禁止指令重排序,所以能在一定程度上保证有序性。
volatile禁止指令重排序有两层意思:
- 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
- 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
总结
与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,在某些情况下将提供优于锁的性能和伸缩性。在某些情况下可以使用 volatile 代替 synchronized 来简化代码。