JVM:Java内存模型

1、硬件效率与一致性

计算机的存储设备和与处理器的运算速度之间差距太大,所以加入一层读写速度尽可能接近处理器运算速度的高速缓存作为内存与处理器之间的缓冲:数据复制到缓存中,运算结束之后再同步到内存中。

告诉缓存解决了处理器和内存速度的矛盾,但是引入了缓存一致性问题。多处理器系统中,每个处理器都有自己的告诉缓存,又共享一个主内存,多个处理的运算任务涉及同一个主内存区域时可能导致各自的缓存数据不一致的情况,因此需要遵循缓存一致性协议。

JVM:Java内存模型

2、Java内存模型

2.1、内存与工作内存

Java内存模型规定所有的变量都存在主内存中,每条线程都有自己的工作内存,线程的工作内存保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中,而不能写主内存中变量。线程之间的变量值传递需要通过主内存完成,线程、主内存、工作内存关系。

JVM:Java内存模型

主内存主要对应Java堆中对象的实例化数据部分。

2.2、内存间交互操作

关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节,Java内存模型中定义了以下8种操作来完成。

2.3、volatile型变量的特殊规则

关键字volatile是Java虚拟机提供的最轻量级的同步机制。

变量被定义成volatile之后又两个特性:

  1. 此变量对所有的线程可见,一条线程修改变量,新值对其他线程立即可见,通过主存传值完成:线程A修改值,向主存回写,线程B在回写完成之后读取,才对B可见。Java运算非原子操作,volatile变量在并发下不安全。
  2. 禁止指令重排序。

2.4、long和double型变量的特殊规则(long和double非原子性协定)

Java内存模型要求lock、unlock、read、load、assign、use、store和write这8个操作都具有原子性,但是对于64位的数据类型long和double,在模型中特别定义了一条宽松的规定:允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行。这样,如果有多个线程共享一个未被声明为volatile的long或double类型的变量,并且同时对它们进行读取和修改操作,那么某些线程可能会读到一个既非原值,也非其他线程修改值得代表了“半个变量”的数值。

3、原子性、可见性、有序性

Java内存模型围绕并发过程中如何保证原子性、可见性和有序性建立的。

原子性:基本数据类型的访问读写具备原子性。

可见性:当一个线程修改了共享变量的值,其他线程都能够得知这个修改。除volatile外,synchronized和final可以实现可见性。

有序性:本线程内观察,所有操作都是有序的,一个线程中观察另一个线程,所有操作都是无序的。前半句是线程内为串行语义,后半句指指令重排序和工作内存和主内存同步延迟现象。

Java提供volatile和synchronized保证有序性。

博客:https://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html

 

“先行发生”原则是判断数据是否存在竞争、线程是否安全的主要依据;

4、先行发生原则

      先行发生原则是指:如果说操作A先行发生于操作B,也就是发生在操作B之前,操作A产生的影响能被操作B观察到。

举例说明:

//以下操作在线程A中执行

i = 1;

//以下操作在线程B中执行

j = i;

//以下操作在线程C中执行

i = 2;

        假设线程A中的操作i=1先行发生于线程B的操作j=i,那么可以确定在线程B的操作执行之后,j一定等于1。因为:根据先行发生原则,i=1的结果可以被B观察到;

现在保持A先行发生于B,线程C出现在A与B之间,但是线程C与B没有先行发生关系。那么j会等于多少呢?答案至不确定。因为线程C对变量i的影响可能会被B观察到,也可能不会。因为两者之间没有先行发生关系;

       先行发生原则就是操作A在时间上或者逻辑上比B先发生,那么B一定能看到A操作带来的影响(修改了共享变量的值等等),那么此时A就是先行发生于B。

博客:https://blog.****.net/bh_xiaoxinba/article/details/52518167