volatile详解

320 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

相信大家对于Java种volatile关键字并不陌生.在多线程的环境种或多或少都用过这种关键字.知道只要变量加上了volatile关键字.变量在多线程环境下就可以保持一致性.但是volatile是如何实现的呢?

上下文切换

在多线程中必不可少的就是上下文切换让我们先来了解一下概念: CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务.但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态.所以任务从保存到再加载的过程就是一次上下文切换.

volatile与synchronized

在多线程并发编程中synchronizedvolatile都扮演着重要角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性.可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.如果volatile变量修饰符使用恰当的话,它比synchronized的使用成本更低,因为它不会引起线程上下文的切换和调度.

volatile定义

Java语言规范第三版中对volatile的定义如下:Java编程语言于允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独的获得这个变量.Java提供的volatile,在某些情况下比锁更加方便.如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的.

实践

volatile是如何保证可见性的呢? 在X86处理器下通过工具获取JIT编译器生成的汇编指令来查看对volatile进行写操作时,CPU是如何操作的.

Java代码如下:

user = new User(); // user是volatile变量

转变成汇编代码,如下:

0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock add1 $0×0,(%esp);

有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码, Lock前缀的指令在多核处理器下会引发了两件事情:

  1. Lock前缀指令会引起处理器缓存回写到内存.
  2. 一个处理器的缓存回写到内存会导致其他处理器的缓存无效.