多线程学习之多线程的可见性

多线程的可见性

  一、深入探究多线程的不确定性因素

1 1、cpu高速缓存导致的极短时间内获取不到想要数据
2 2、cpu指令重排导致之后的自行优化导致数据混乱;当然,这种情况值存在于多线程的基础上,单个线程是不会出现这种问题的。
3 3、JIT会把多次方法区中多调用或者多次循环的数据进行换存,然后自行优化;这里缓存也会导致后来改变的数据无法被正常的获取。

  首先来说cpu的高速缓存,它是介于我们物理内存与程序之间。

  CPU高速缓存(英语:CPU Cache,在本文中简称缓存)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。

  其容量远小于内存,但速度却可以接近处理器的频率。

  当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效), 则要先把内存中的相应数据载入缓存,再将其返回处理器。
 
  缓存之所以有效,主要是因为程序运行时对内存的访问呈现局部性(Locality)特征。这种局部性既包括空间局部性(Spatial Locality),也包括时间局部性(Temporal Locality)。
 
  有效利用这种局部性,缓存可以达到极高的命中率。
 
  在处理器看来,缓存是一个透明部件。因此,程序员通常无法直接干预对缓存的操作。但是,确实可以根据缓存的特点对程序代码实施特定优化,从而更好地利用缓存。
  
  这里说吧内存中的相应数据载入缓存,再将其返回处理器。在多线程的场景下,就会导致在极短的时间内导致数据获取不到。
 

  CPU指令重排:这种情况在单个线程下怎么重排都无所谓,不会出现值混乱,但是在多线程的环境下就可能出现值混乱。

 1 //就是说Java 编译器可能重新排列源代码执行的顺序来优化编译的表现,比如
 2 
 3 int a = 0, 
 4 int b = a + 1;
 5 int c = 2;
 6 
 7 //在编译成bytecode 以后,执行的顺序可能是
 8 int c = 2;
 9 int b = a + 1;
10 int a = 0;

  JIT执行模型:

 

   二、JMM内存模型

    Java内存模型中规定:
    > 对某个 volatile 字段的写操作 happens-before 每个后续对该 volatile 字段的读操作。
    > 对 volatile 变量 v 的写入,与所有其他线程后续对 v 的读同步;
    
    Volatile如何实现它的语义:
    > 禁止缓存;
    volatile变量的访问控制符会加个ACC_VOLATILE
    https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5
    > 对volatile变量相关的指令不做重排序;
 
    以此来保证多线程的可见性。

猜你喜欢

转载自www.cnblogs.com/zhanvo/p/11845693.html