Java内存模型
2019-08-21 本文已影响9人
xiaolyuh
线程之间的通信和同步
线程之间的通信和同步是并发编程领域的关键问题。
线程之间的通信
通信是指线程之间以何种机制来交换信息。线程之间的通信一般有两种方式:共享内存和消息传递。在java中典型的消息传递方式就是wait()和notify()。
线程之间的同步
同步是指程序用于控制不同线程之间操作发生相对顺序的机制。
在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行,如加锁和CAS。
在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。
java内存模型的抽象结构

工作内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。工作内存主要是为了解决CPU和内存之间的运算能力相差甚远的问题。它和现代计算机的内存结构很相似,如图:

现代的处理器使用写缓冲区临时保存向内存写入的数据。写缓冲区可以保证指令流水线 持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以 批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总 线的占用。但是它存在一个问题:处理器对内存的读/写操作的执行 顺序,不一定与内存实际发生的读/写操作顺序一致,这个问题在java内存模型中也存在。为了具体说明,请看下面的表:

a和b的初始值为0。处理器A和处理器B同时将,参数a和b加载到高速缓存,处理器A执行
a=1;x=b;
、处理器B执行b=2;y=a
。这时A处理器中的b=0,B处理器中的a=0,所以最后得到的结果就是x=y=0。
Java内存模型的实现
在JVM内部,Java内存模型把内存分成了两部分:线程栈区和堆区。

JVM中运行的每个线程都拥有自己的线程栈,线程栈包含了当前线程执行的方法调用相关信息和所有局部变量,我们也把它称作调用栈。一个线程只能读取自己的线程栈,也就是说,线程中的本地变量对其它线程是不可见的。即使两个线程执行的是同一段代码,它们也会各自在自己的线程栈中创建局部变量,因此,每个线程中的局部变量都会有自己的版本。
堆中的对象可以被多线程共享。如果一个线程获得一个对象的应用,它便可访问这个对象的成员变量。如果两个线程同时修改这个成员编码,那么就会引发并发问题,如图的Object3
。
Java内存模型带来的问题
- 共享对象对各个线程的可见性
- 共享对象的竞争现象