volatile的定义
Java编程语言中允许线程访问共享变量。为了确保共享变量能被一致地和可靠的更新,线程必须确保它是排他性的使用此共享变量,通常都是获得对这些共享变量强制排他性的同步锁。
Java编程语言提供了另一种机制,volatile
域变量,对于某些场景的使用这要更加的方便。
可以把变量声明为volatile
,以让Java内存模型来保证所有线程都能看到这个变量的同一个值。
volatile的作用
- 保证变量的可见性
volatile关键字的作用就是保证共享变量的可见性。什么是可见性呢,就是一个线程读变量,总是能读到它在内存中的最新的值,也就是说不同的线程看到的一个变量的值是相同的。CPU都是有行缓存的,volatile能让行缓存无效,因此能读到内存中最新的值。
- 保证赋值操作的原子性
原子性就是不能被线程调度打断的操作,是线程安全的操作,对于原子性操作,即使在多线程环境下,也不用担心线程安全问题或者数据不一致的问题。有些变量的赋值本身就是原子性的,比如对boolean
,对int
的赋值,但是像对于long
或者double
则不一定,如果是32位的处理器,对于64位的变量的操作可能会被分解成为二个步骤:高32位和低32位,由此可能会发生线程切换,从而导致线程不安全。如果变量声明为volatile
,那么虚拟机会保证赋值是原子的,是不可被打断的。
- 禁止指令重排(有序性)
正常情况下,虚拟机会对指令进行重排,当然是在不影响程序结果的正确性的前提下。volatile
能够在一定程度上禁止虚拟机进行指令重排。还有就是对于volatile
变量的写操作,保证是在读操作之前完成,假设线程A来读变量,刚好线程B正在写变量,那么虚拟机会保证写在读之前完成。 比如:
private volatile boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
public void getFlag() {
return flag;
}
假设线程A来调用setFlag(true)
,线程B同时来调用getFlag
,对于一般的变量,是无法保证B能读到A设置的值的,因为它们执行的顺序是未知的。但是像上面,加上volatile
修饰以后,虚拟机会保证,线程A的写操作在线程B的读操作之前完成,换句话,B能读到最新的值。当然了,用锁机制也能达到同样的效果,比如在方法前面都加上synchronized
关键字,但是性能会远不如使用volatile
。<