在学习java多线程并发编程前,必须要了解java内存模型,只有了解java内存模型,才能知道为什么多线程并发时会出现数据不一致,什么时候需要加锁同步等各种问题。下面只是简单阐述下java内存模型及其相关的概念。
java的并发采用的是共享内存模型(而非消息传递模型)。
Java内存模型(Java Memory Model)描述了Java程序中各种变量(共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。Java内存模型的抽象示意图如下:
内存模型
图中的共享变量为:实例变量和静态变量。(局部变量是线程私有的不存在竞争)
从图中看,线程A和线程B进行通讯必须通过主内存
注意:
指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。编译器、处理器也遵循这样一个目标。注意是单线程。多线程的情况下指令重排序就会给程序员带来问题。
重排序分三种类型:
Paste_Image.png
内存可见性简单描述:当主内存中的一个共享变量在多个线程的本地内存中都存在副本,如果一个线程修改共享变量,其它线程也应该能看到被修改后的值。
要实现共享变量的可见性,必须实现两点:
JMM使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作就必须存在happens-before关系。
happens-before规则如下:
注意,两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。
as-if-serial定义重排序的规则。 as-if-serial语义的意思:不管怎么重排序,程序的执行结果都不能被改变。编译器、runtime和处理器都必须遵守as-if-serial规则。
例如:
int a=2; //A
int b=3; //B
int c=a*b; //C
Paste_Image.png
注意:
上面程序的happens-before的关系:
在这里A happens-before B, 但实际执行B可以排在A之前执行。JMM仅仅要求前一个操作对后一个操作可见,且前一个操作按顺序排在第二个操作之前。这里A的执行结果不需要对操作B可见。