java内存模型
一、计算机的高速缓存模型
现在的计算机基本都是多核的,比如我用的8核,上图只有两核。数据最根本当然是存储再硬盘上的,但是硬盘的读取速度很慢。所以都是先将程序运行的数据加载到内存条(RAM中)的。早期的计算机,CPU和RAM是直接交互的,因为早期的CPU的性能还不像现在这样,非常好。有一个叫做摩尔定律的,大概的意思是说现在的科技发展,每间隔18个月,CPU的性能会翻一倍。慢慢的CPU的性能就比RAM高出很多了,这样导致RAM不能很好的发挥CPU的性能。现在的计算机大多都再CPU和RAM之间加了一个CPU高速缓存。但是这个缓存非常贵...如果打开任务管理器,是可以看到的,如下图
二、JMM JAVA内存模型
了解CPU高速缓存模型是因为JMM和它很像,RAM中存放的是一些程序变量。多个线程可能会共享变量。每个线程都有自己额工作内存。共享变量其实是先复制到工作内存中给线程用,最后再线程操作ok后再保存回去的。但是线程A,B,C如果共享了同一个变量,再不做任何代码级别的操作的时候,他们之间的共享变量副本是不会互相感知的,这个时候就可能出现线程安全相关的问题了。比如下面的代码示例
package com.lyb.jmm; /** * @ClassName Jmm * @Description * @Author Lyb * @Date 2019/12/4 13:48 * @Version V1.0 **/ public class Jmm { private static boolean isPrepared =false; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { System.out.println("waiting data..."); while(!isPrepared){ } System.out.println("========================== Success"); } }).start(); Thread.sleep(2000); new Thread(new Runnable() { @Override public void run() { System.out.println("prepare data..."); isPrepared=true; System.out.println("prepare Success"); } }).start(); } }
预想:两个线程,第一个在等待数据,如果准备好了的标记为true了,退出循环,输出==================== Success,否则一直循环。第二个线程,准备数据,将标记改为true,输出prepare Success。两个线程共享一个isPrepared静态变量。按理说程序会正常走,知道输出==================== Success,但是程序运行结果如下图。
很显然,第一个线程没有感知到第二个线程已经准备数据ok了,没有感知到第二个线程堆isPrepared这个共享变量进行的修改。所以程序运行没有按照我们预想的进行。
解决。我们在这个共享的静态变量上加一个关键字volatile,就可以将线程间不能相互感知的变量变的能相互感知了
四、JAVA内存模型中原子操作
T1从RAM中read到的共享变量。
读到变量之后load到自己的工作内存中
线程按照代码use变量比如本例中的对变量取反,并while循环空转
T1也是先Read变量,load到工作内存,use,不同的是这里有一个assign赋值操作,会将工作内存中load的值改变为true,然后将改变后的值store到主内存中,最后写回到主内存中write。图中的read和write都是在主内存中的过程,load和store分别是将read到的数据加载到工作内存,将修改后的变量保存到主内存,可以说是两两对立的一种关系。store之后变量已经在主内存中了,图不好画,理解就ok
下图是JMM中的原子操作概念
五、volatile关键字的底层原理
看了上面的JMM原子性操作之后,我们就可以来理解volatile关键字了。总线就是连接RAM和CPU之间的连线,可能是主板和CPU,因为RAM大多在主板上。在多核CPU读取主内存的数据的时候其实就是通过总线这个实际的连线进行的。现在的计算机总线基本都回遵循MESI缓存一致性协议。volatile关键字的底层就是每次在改变一个变量的时候,就会立即将这个变量重新store到主内存中,并且在cpu总线嗅觉监听中加上这个变量,当其他线程中也有这个变量的时候,会立即将这个变量失效掉。程序运行的时候发现需要用到的变量被失效了,会重新去内存中读取变量。同时会给这个变量加锁。不过这个锁的粒度很小,就是锁在store的过程中,因为cpu对ram的读取操作时特别快的,所以在这个锁定的时候,其他线程基本无需等待锁的释放,因为其他现在再来读取这个变量的时候store过程基本已经完成了,锁时被释放的状态。(在很久之前的计算机中是对总线加锁的,当一个变量被读取之后,就锁定,等读取这个变量的线程结束了才释放。其他线程才能来读,强行将多线程改为了并列执行)然而volatile这个关键字可以说是很轻量级别的锁了。