Java内存模型与volatile
2017-08-05 20:18
162 查看
Java内存模型(JMM)
在介绍volatile之前,先介绍一下java内存模型(JMM)。如下图所示:
每个Java线程在运行的过程中,都有一个与之对应的工作内存,这个内存空间是线程私有的。
当多个线程同时修改同一个对象时,线程会首先从主内存里面取出对象到工作内存当中去。然后更改完工作内存之后,再更新到主内存当中去。因此,对于普通的变量来说,在多线程操作的过程中,如果内部不做线程安全的控制,就存在着线程安全的问题。
volatile简单介绍
volatile关键字保证的是一种可见性。就是当一个线程修改了一个volatile类型的变量,在另外一个线程操作之前,会立即通知到其数据已经更新。这里面简单的介绍一下相关的语义:
class VolatileExample {
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; //1
flag = true; //2
}
public void reader() {
if (flag) { //3
int i = a; //4
……
}
}
} 假设线程A执行writer()方法之后,线程B执行reader()方法。根据happens before规则,这个过程建立的happens before 关系可以分为两类:
根据程序次序规则,1 happens before 2; 3 happens before 4。
根据volatile规则,2 happens before 3。
根据happens before 的传递性规则,1 happens before 4。
上述happens before 关系的图形化表现形式如下:
在上图中,每一个箭头链接的两个节点,代表了一个happens before 关系。黑色箭头表示程序顺序规则;橙色箭头表示volatile规则;蓝色箭头表示组合这些规则后提供的happens before保证。
这里A线程写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile变量之前所有可见的共享变量,在B线程读同一个volatile变量后,将立即变得对B线程可见。
参见:http://www.infoq.com/cn/articles/java-memory-model-4
一个有趣的例子
public class Novisibility {
private static boolean ready;// 在server模式下,由于JVM优化,造成ReaderThread永远无法看到ready=true, 所以while会一直循环
private static int number;
private static class ReaderThread extends Thread {
@Override
public void run() {
while (!ready)
;
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
Thread.sleep(1000);
number = 42;
ready = true;
Thread.sleep(10000);
}
} 通过运行以上代码可以看见,程序无法退出。这是由于在-server模式下,JVM对指令进行了优化,造成了子线程中的ready永远都不知道ready的值已经变化。
volatile数组变量的可见性
这里面以 CopyOnWriteArrayList 源代码为例,介绍一下其内部是如何使用volatile关键字的。
通过数组的整体替换,才可以保证对其他线程的可见性。这里如果仅仅修改了索引为index的元素值。那么其他线程是无法立马看到结果的。
对于volatile 仅仅对数组对象的可见性,而不是其元素,请参见:http://ifeve.com/volatile-array-visiblity/
链接:http://moguhu.com/article/detail?articleId=24
在介绍volatile之前,先介绍一下java内存模型(JMM)。如下图所示:
每个Java线程在运行的过程中,都有一个与之对应的工作内存,这个内存空间是线程私有的。
当多个线程同时修改同一个对象时,线程会首先从主内存里面取出对象到工作内存当中去。然后更改完工作内存之后,再更新到主内存当中去。因此,对于普通的变量来说,在多线程操作的过程中,如果内部不做线程安全的控制,就存在着线程安全的问题。
volatile简单介绍
volatile关键字保证的是一种可见性。就是当一个线程修改了一个volatile类型的变量,在另外一个线程操作之前,会立即通知到其数据已经更新。这里面简单的介绍一下相关的语义:
class VolatileExample {
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; //1
flag = true; //2
}
public void reader() {
if (flag) { //3
int i = a; //4
……
}
}
} 假设线程A执行writer()方法之后,线程B执行reader()方法。根据happens before规则,这个过程建立的happens before 关系可以分为两类:
根据程序次序规则,1 happens before 2; 3 happens before 4。
根据volatile规则,2 happens before 3。
根据happens before 的传递性规则,1 happens before 4。
上述happens before 关系的图形化表现形式如下:
在上图中,每一个箭头链接的两个节点,代表了一个happens before 关系。黑色箭头表示程序顺序规则;橙色箭头表示volatile规则;蓝色箭头表示组合这些规则后提供的happens before保证。
这里A线程写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile变量之前所有可见的共享变量,在B线程读同一个volatile变量后,将立即变得对B线程可见。
参见:http://www.infoq.com/cn/articles/java-memory-model-4
一个有趣的例子
public class Novisibility {
private static boolean ready;// 在server模式下,由于JVM优化,造成ReaderThread永远无法看到ready=true, 所以while会一直循环
private static int number;
private static class ReaderThread extends Thread {
@Override
public void run() {
while (!ready)
;
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
Thread.sleep(1000);
number = 42;
ready = true;
Thread.sleep(10000);
}
} 通过运行以上代码可以看见,程序无法退出。这是由于在-server模式下,JVM对指令进行了优化,造成了子线程中的ready永远都不知道ready的值已经变化。
volatile数组变量的可见性
这里面以 CopyOnWriteArrayList 源代码为例,介绍一下其内部是如何使用volatile关键字的。
/** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;对于CopyOnWriteArrayList 内部保存数据的数组,使用了volatile 关键字。
public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); E oldValue = get(elements, index); if (oldValue != element) { int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return oldValue; } finally { lock.unlock(); } }上面是CopyOnWriteArrayList 设置元素的代码,可以看出,这个并不是简单的在index位置上设置元素。而是生成了一个新的数组元素,然后将当前的array设置成新的newElements 。
通过数组的整体替换,才可以保证对其他线程的可见性。这里如果仅仅修改了索引为index的元素值。那么其他线程是无法立马看到结果的。
对于volatile 仅仅对数组对象的可见性,而不是其元素,请参见:http://ifeve.com/volatile-array-visiblity/
链接:http://moguhu.com/article/detail?articleId=24
相关文章推荐
- 深入理解Java内存模型(四)——volatile
- 全面理解Java内存模型(JMM)及volatile关键字
- 深入理解Java内存模型 - volatile
- 深入理解Java内存模型——volatile
- 深入理解Java内存模型(四)——volatile
- Java内存模型与volatile关键字
- Java内存模型--(四)volatile的内存语义
- 深入理解Java内存模型(四)——volatile
- java内存模型-volatile
- 【死磕Java并发】-----Java内存模型之分析volatile
- 深入理解Java内存模型(四)——volatile
- Java内存模型深度解析:volatile--转
- 多线程读书笔记二(java内存模型、volatile变量、内存模型与synchronized、CAS)
- java内存模型,多线程三大特性,volatile,Threalocal,线程池
- 深入理解Java内存模型——volatile
- 深入理解java内存模型(四)——volatile
- 深入理解JVM读书笔记五: Java内存模型与Volatile关键字
- java内存模型与volatile关键字
- java基础知识(五)java内存模型和volatile关键字
- volatile关键字 / java内存模型 /AtomicInteger原子类 / ThreadLock