当一个变量定义为volatile后,此变量对全部的线程具备可见性。这里的可见性是指当一个线程修改了这个变量的值,新值对于其余线程来讲是能够当即得知的。安全
每次使用volatile变量前都必须先从主内存刷新最新的值,这保证能看见其余线程对变量所作的修改后的值。每次修改volatile变量后都必须同步回主内存,这保证其余线程能够看到本身对变量所作的修改。正是这两条规则保证了volatile变量的可见性。多线程
volatile要求线程每次从共享内存中读取变量,而不是私有内存中读取。并发
volatile能够禁止指令重排序优化,重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。可是重排序也须要遵照必定规则,重排序操做不会对存在数据依赖关系的操做进行重排序。好比:a=1;b=a; 这个指令序列,因为第二个操做依赖于第一个操做,因此在编译时和处理器运行时这两个操做不会被重排序。可是在多线程环境下,可能出现问题。以下示例:ide
Map configOptions; volatile boolean initialized = false; // 如下代码在线程A中执行 // 线程A完成配置信息后,initialized改成true configOptions = new HashMap(); processConfigOptions(configOptions); initialized = true; // 如下代码在线程B中执行 // 等待initialized为true,表示线程A已完成配置 while (!initialized) { sleep(); } // 线程A完成配置后,线程B能够开始doSomething(); doSomething();
initialized必须定义为volatile,不然可能出现线程A先赋值initialized为true,然后执行配置。性能
volatile保证了在赋值initialized以前,先执行完其前面的代码。优化
volatile 的读性能消耗与普通变量几乎相同,可是写操做稍慢,由于它须要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。spa
以下示例,20个线程分别将变量race自加100次,运算结果race的值小于20000 。线程
public class VolatileTest { public static volatile int race; public static void increase() { race++; } private static final int threads_count = 20; public static void main(String[] args) { Thread[] threads = new Thread[threads_count]; for (int i=0; i<threads_count; i++) { threads[i] = new Thread( new Runnable() { @Override public void run() { for (int i=0; i<100; i++) increase(); } } ); threads[i].start(); } System.out.println(race); } }
示例中,race++不是一个原子操做,其步骤分解以下:code
1)从内存中取出race的值;blog
2)计算race的新值(加1);
3)将race的值写入内存中。
race是volatile修饰的,这保证了其可见性,即全部的线程在使用该变量时,都是从共享内存中读取。
但volatile不保证原子性,一个线程在进行第二步操做时,其它线程能够读取或修改race的值,这就出现了不一样步的现象。
所谓原子性,是指一个操做或多个操做要么所有执行完成且执行过程不被中断,要么就不执行。例如,若race++是原子性的,某个线程在执行该操做时,其余线程不能干涉,必须等待其执行完毕。