众所周知,volatile是java语言提供的一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程,如果变量被声明为volatile类型后,编译器和运行时都会注意到这个变量是共享的,因此不会将该变量的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile的变量时总是返回最新写入的值.。
volatile保证可见性
如果某个变量被volatile修饰之后,那么久具备了两层含义:
1.对于多线程操作而言,单个线程对该变量的修改,对其他线程是立即可见的。
因为使用volatile关键字修饰的变量被修改后会被立即刷新到内存,当其他线程读取该变量时,会使其他线程对应的工作缓存的变量无效,重新去内存中读取变量,因此总能读到最新的变量值。
2..禁止进行指令重排序
简单的说,就是在该指令前面的指令一定会先执行,在该指令后面的指令一定是该条指令运行后才能执行,但是该条指令前面的指令可以指令重排序,该指令后面的指令也是可以重排序的
volatile不保证原子性
下面拿一段代码为例
public class VolitileTest { public volatile int visitCount = 0; public void increase() { visitCount++; } public static void main(String[] args) { final VolitileTest test = new VolitileTest(); for (int i = 0; i < 200; i++) { new Thread() { @Override public void run() { for (int j = 0; j < 10000; j++) test.increase(); } }.start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(test.visitCount); } }
运行结果为1985687,发现并不是200000,其实很容易理解,虽然某个线程修改变量值后会立即将更新的值写到内存,但是并不能保证是原子操作,他只能保证读取的是最新的值。假设某个时刻visitCount变量的值为100,线程1对变量执行加一操作,线程1先读取变量的原始值,然后线程1阻塞了,此时线程2也去读取变量的值,显然此时读到的也是100,然后线程2进行自增操作,将101写入内存,由于线程1已经读取了visitCount的值,并不会再去内存读取一边,此时它恢复运行状态,也执行自增,同样的将101写入内存,此时会发现虽然两个线程都执行了自增操作,但是值却只加了一。
volatile的使用场景
1.对变量的写操作不依赖于当前值
2.该变量没有包含在其他变量的不变式中
3.访问变量时不需要加锁