volatile 可以看做是一种轻量级的synchronized实现,volatile实现的仅仅是
synchronized
的部分功能,但是开销较synchronized
小。特定的情形下,使用volatile会更为合适。
synchronized
提供了两种主要特性:互斥 和可见性。互斥即同一时刻只允许一个线程持有某个特定的锁,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。
volatile具有 synchronized
的可见性特性,但是不具备互斥性和原子特性。这就是说多个线程能够自动发现 volatile 变量的最新值。
关于可见性解释如下:
类的成员变量是存储在堆内存中的,堆内存的运行速度较栈内存的运行速度要慢的多。所以Java多线程在处理成员变量时,是在每个线程的工作内存中拷贝一份成员变量,线程内完成对成员变量的操作后再写入主内存中。
上面的读取,写入都不是原子的,ThreadA对成员变量的修改,ThreadB无法立即看到,因此ThreadB工作内存中的成员变量还是旧的值,这就是多线程所产生的数据不一致。
volatile修饰的成员变量,线程读取的不是线程工作内存中的副本,而是直接从主内存中读取最新的值。volatile变量修改时,会立即写入到主内存中。
关于volatile还有一点需要解释,就是happens-before原则,先看下面的例子:
package multiThread; class VolatileExample { static int a, i = 0; static volatile boolean flag = false; public static void writer() { a = 1; // 1 flag = true; // 2 } public static void reader() { if (flag) { // 3 i = a; // 4 } System.out.println("i= " + i); } public static void main(String [ ] args) { for (int i = 0; i < 200; i++) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { writer(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { reader(); } }); thread1.start(); thread2.start(); } } }
上面的例子运行结果全为1,而没有出现0。
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。程序中写在前面的语句可能会被放在后面执行,happens-before原则对重排序做了限制,符合happens-before原则时,前一个操作执行的结果必须对后一个操作可见。
happens-before原则中与volatile相关的部分:
volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。
传递性:如果A happens- before B,且B happens- before C,那么A happens- before C
上述例子程序中的的happens before 关系可以分为两类:
根据传递性规则,1 happens before 2; 3 happens before 4。
根据volatile规则,2 happens before 3。
根据happens before 的传递性规则,1 happens before 4。
上述happens before 关系的图形化表现形式如下:
原文:http://www.cnblogs.com/lnlvinso/p/4614719.html