Welcome 微信登录

首页 / 软件开发 / JAVA / 深入理解Java内存模型(四) volatile

深入理解Java内存模型(四) volatile2014-05-31 infoq 程晓明volatile的特性

当我们声明共享变量为volatile后,对这个变量的读/写将会很特别。理解 volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个 读/写操作做了同步。下面我们通过具体的示例来说明,请看下面的示例代码:

class VolatileFeaturesExample {volatile long vl = 0L;//使用volatile声明64位的long型变量 public void set(long l) {vl = l; //单个volatile变量的写} public void getAndIncrement () {vl++;//复合(多个)volatile变量的读/写}public long get() {return vl; //单个volatile变量的读}}
假设有多个线程分别调用上面程序的三个方法,这个程序在语意上和下面程序等价:

class VolatileFeaturesExample {long vl = 0L; // 64位的long型普通变量 public synchronized void set(long l) { //对单个的普通 变量的写用同一个监视器同步vl = l;} public void getAndIncrement () { //普通方法调用long temp = get(); //调用已同步的读方法temp += 1L;//普通写操作set(temp); //调用已同步的写方法}public synchronized long get() { //对单个的普通变量的读用同一个监视器同步return vl;}}
如上面示例程序所示,对一个volatile变量的单个读/写操作,与对一个普通变量的读/写操作 使用同一个监视器锁来同步,它们之间的执行效果相同。

监视器锁的happens-before规则保证释 放监视器和获取监视器的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到( 任意线程)对这个volatile变量最后的写入。

监视器锁的语义决定了临界区代码的执行具有原子 性。这意味着即使是64位的long型和double型变量,只要它是volatile变量,对该变量的读写就将具有原 子性。如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。

简而言之,volatile变量自身具有下列特性:

可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子 性。

volatile写-读建立的happens before关系

上面讲的是volatile变量自身的特性,对程序员来 说,volatile对线程的内存可见性的影响比volatile自身的特性更为重要,也更需要我们去关注。

从JSR-133开始,volatile变量的写-读可以实现线程之间的通信。

从内存语义的角度来说 ,volatile与监视器锁有相同的效果:volatile写和监视器的释放有相同的内存语义;volatile读与监视 器的获取有相同的内存语义。

请看下面使用volatile变量的示例代码:

class VolatileExample {int a = 0;volatile boolean flag = false; public void writer() {a = 1; //1flag = true; //2} public void reader() {if (flag) {//3int i =a; //4……}}}