Ⅰ java多线程的内存模型
硬件的内存模型
物理机并发处理的方案对于jvm的内存模型实现,也有很大的参考作用,毕竟jvm也是在硬件层上来做事情,底层架构也决定了上层的建筑建模方式。
计算机并发并非只是多个处理器都参与进来计算就可以了,会牵扯到一些列硬件的问题,最直接的就是要和内存做交互。但计算机的存储设备与处理器的预算速度相差太大,完全不能满足处理器的处理速度,怎么办,这就是后续加入的一层读写速度接近处理器运算速度的高速缓存来作为处理器和内存之间的缓冲。
高速缓存一边把使用的数据,从内存复制搬入,方便处理器快速运算,一边把运算后的数据,再同步到主内存中,如此处理器就无需等待了。
高速缓存虽然解决了处理器和内存的矛盾,但也为计算机带来了另一个问题:缓存一致性。特别是当多个处理器都涉及到同一块主内存区域的时候,将可能会导致各自的缓存数据不一致。
那么出现不一致情况的时候,以谁的为准?
为了解决这个问题,处理器和内存之间的读写的时候需要遵循一定的协议来操作,这类协议有:MSI、MESI、MOSI、Synapse、Firefly以及DragonProtocol等。这就是上图中处理器、高速缓存、以及内存之间的处理方式。
另外除了高速缓存之外,为了充分利用处理器,处理器还会把输入的指令码进行乱序执行优化,只要保证输出一致,输入的信息可以乱序执行重组,所以程序中的语句计算顺序和输入代码的顺序并非一致。
JVM内存模型
上面我们了解了硬件的内存模型,以此为借鉴,我们看看jvm的内存模型。
jvm定义的一套java内存模型为了能够跨平台达到一致的内存访问效果,从而屏蔽掉了各种硬件和操作系统的内存访问差异。这点和c和c++并不一样,C和C++会直接使用物理硬件和操作系统的内存模型来处理,所以在各个平台上会有差异,这一点java不会。
java的内存模型规定了所有的变量都存储在主内存中,java课程发现每个线程拥有自己的工作内存,工作内存保存了该线程使用到的变量的主内存拷贝,线程对变量所有操作,读取,赋值,都必须在工作内存中进行,不能直接写主内存变量,线程间变量值的传递均需要主内存来完成。
Ⅱ Java学习——volatitle关键字
内存模型的概念与作用
在计算机执行程序时,指令在CPU中执行,涉及数据读取和写入。数据存放在主存(物理内存),这会导致速度差异,因此CPU有高速缓存。程序运行时,数据从主存复制到高速缓存,CPU直接从缓存读取和写入,运算结束后刷新缓存到主存。多线程中,高速缓存间的数据不一致即为缓存一致性问题。共享变量是多线程访问的变量,可能引发问题。
解决缓存不一致的策略有:在总线上加LOCK#锁,或使用缓存一致性协议(如Intel的MESI协议)。LOCK#锁阻塞其他CPU访问内存,MESI协议确保缓存副本一致性。
并发编程中的关键概念
并发编程中面临原子性、可见性与有序性问题。原子性指操作要么全部执行,要么不执行。可见性指多线程修改变量值后,其他线程能立即看到修改。有序性指指令按代码顺序执行。
Java内存模型概述
Java内存模型通过规范定义变量访问规则,屏蔽硬件和操作系统差异,确保跨平台一致性。模型规定所有变量在主存,每个线程有工作内存。线程操作在工作内存,不能直接操作主存,且不能访问其他线程工作内存。
Java内存模型保证:基本读取和赋值原子性;通过volatile关键字保证可见性;synchronized和Lock保证有序性。
volatile关键字的特性与局限
volatile关键字提供两层语义:保证不同线程对变量操作的可见性,禁止指令重排序。其局限在于不能保证操作原子性。
自增操作的原子性问题
volatile关键字保证可见性,但无法保证操作原子性。自增操作可能被分割执行,导致不一致结果。Java并发包提供原子操作类,利用CAS(Compare And Swap)实现原子性。
volatile的指令重排序限制
volatile关键字禁止指令重排序,确保在读或写操作前,前操作更改可见;写操作后,后续操作未执行。这在一定程度上保证了有序性。
总结
理解内存模型、并发问题与Java内存模型对于编写高效、一致的并发代码至关重要。利用volatile、synchronized与Lock关键字可有效管理原子性、可见性与有序性,确保多线程程序的正确性与性能。