多线程笔记(六):多线程----有序性

概念    

       所谓有序性,是指程序在执行过程中的先后顺序,由于Java在编译器以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。

实例

int x = 10;
int y = 0;
x++;
y = 20;

       上面这段代码定义了两个 int 类型的变量 x 和 y,对 x 进行自增操作,对 y 进行赋值操作,从编写程序的角度来看上面的代码肯定是顺序执行下来的,但是在 JVM 真正地运行这段代码的时候未必会是这样的顺序,比如 y=20 语句有可能会在 x++ 语句的前面得到执行,这种情况就是我们通常所说的 指令重排序

       一般来说,处理器为了提高程序的运行效率,可能会对输入的代码指令做一定的优化,它不会百分之百的保证代码的执行顺序严格按照编写代码中的顺序来进行,但是它会保证程序的最终运算结果是编码时所期望的那样,比如上文中 x++ 与 y=20 不管它们的执行顺序如何,执行完上面的四行代码之后得到的结果肯定都是 x=11,y=20。

       指令重排序原则:保证不影响代码执行语义的前提下,实现重排序,从而提升运行效率。当然对指令的重排序要严格遵守指令之间的数据依赖关系,并不是可以任意进行重排序的。

原因分析

1.硬件层面分析

  1.如果想要进一步了解有序性,那么我们需要来了解一下 CPU高速缓存 相关内容

   CPU高速缓存

        线程是CPU调度的最小单元,CPU在调度执行指令时,并不是仅仅只靠 CPU 处理器来完成,CPU 会与内存之间进行交互。即当我们执行一个指令时,需要 CPU 从内存中加载数据(读取、存储数据等操作)来完成,在这一系列操作中,则会涉及到 IO操作。

        然而CPU运算处理速度与内存的读写速度差异很大,CPU速度明显高于内存速度。为了解决这种差异,充分利用CPU的处理速度,所以CPU缓存应运而生。它是介于CPU处理器和内存之间的临时数据交换的缓冲区。

        CPU 缓存,分为:一级缓存二级缓存三级缓存。如下图所示

        众所周知,在如今的计算机时代,CPU都是多核心同时运行。CPU通过IO操作从主内存中加载数据,首先会从缓存中取值;如果缓存中依然拿不到值,那么它会从主内存中去加载数据。流程如图所示

  2.了解CPU高速缓存后,便会涉及到缓存一致性的问题

  缓存一致性问题

        缓存 cache 给系统带来性能上飞跃的同时,也引入了新的问题:缓存一致性问题。

        场景:设想CPU一共有两个核,core1 和 core2 。以i++为例,i 的初始值是0。那么在开始每个核都存储了 i 的值 0,当core1 做 i++ 的时候,其缓存中的值变成了1,即使马上回写到主内存,那么在回写之后core2缓存中的 i 值依然是0,其执行i++,回写到内存就会覆盖第一块内核的操作,使得最终的结果是1,而不是预期中的2。

   解决方法:共有如下 2 种解决方法

    1.总线锁

       某个CPU核心,去访问数据操作,往总线发送一个 lock 操作。其他核心请求数据操作时,便会被阻塞(总线锁相当于排它锁/悲观锁)

       缺点:导致性能问题

       图示:如上图所示,在总线处加一把锁,来阻止其他线程去访问数据

    2.缓存锁

        Core 1缓存一条数据,Core 2也缓存着这条数据,当 Core 1 修改这条数据时,就会触发缓存锁。

        缓存锁怎么实现缓存一致性问题呢?为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,常见的协议有 MSI,MESI,MOSI等。其中最经典的则是 MESI 协议。

        图示:如下图所示,缓存一致性协议所处位置

          

         如需了解具体的 MESI 协议,请自行百度了解。此处不作更深层的了解。

2.应用层面分析

    针对以上这些问题,Java的并发采用的是共享内存模型。

    什么是JMM?

       JMM 即为 JAVA 内存模型(Java Memory Model),是在 JVM 中定义的一个抽象内存模型。

       因为在不同的硬件生产商和不同的操作系统下,内存的访问逻辑有一定的差异,结果就是当你的代码在某个系统环境下运行良好,并且线程安全,但是换了个系统就出现各种问题。Java内存模型,就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果。JMM从 Java 5 开始的 JSR-133 发布后,已经成熟和完善起来。

       通俗讲就是:不同操作系统,不同CPU架构,协议支持有所不同。有些 CPU 支持 MESI 协议,有些支持强一致性协议 等,所以不同的硬件架构存在一些不同的硬件差异。为了屏蔽硬件和操作系统内存访问存在的这些差异,来实现Java程序在各个平台上能够达到一致性的访问效果,所以在JVM层面上抽象出一个JMM的抽象内存模型。

     JMM内存划分

        JMM规定了内存主要划分为主内存工作内存两种。

        此处的主内存和工作内存跟JVM内存划分(堆、栈、方法区)是在不同的层次上进行的。如果非要对应起来,主内存对应的是Java堆中的对象实例部分,工作内存对应的是栈中的部分区域,从更底层的来说,主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存。

        JMM 内存执行流程,如下图所示。

        

       JVM在设计时候考虑到,如果JAVA线程每次读取和写入变量都直接操作主内存,对性能影响比较大,所以每条线程拥有各自的工作内存,工作内存中的变量是主内存中的一份拷贝,线程对变量的读取和写入,直接在工作内存中操作,而不能直接去操作主内存中的变量。

       但是这样就会出现一个问题,当一个线程修改了自己工作内存中变量,对其他线程是不可见的,会导致线程不安全问题。因为 JMM 制定了一套标准来保证开发者在编写多线程程序的时候,能够控制什么时候内存会被同步给其他线程。

     内存交互操作

      内存交互操作有8种,JVM虚拟机实现必须保证每一个操作都是原子的,不可在分的(double、long类型在某些平台有例外)

  1. lock     (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  2. unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  3. read    (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用
  4. load     (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  5. use      (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  6. assign  (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  7. store    (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的 write 使用
  8. write  (写入):作用于主内存中的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中

      JMM 对这八种指令的使用,制定了如下规则:

  1. 不允许 read 和 load、store 和 write 操作之一单独出现。即使用了 read 必须 load ,使用了 store 必须 write
  2. 不允许线程丢弃他最近的 assign 操作,即工作变量的数据改变了之后,必须告知主存
  3. 不允许一个线程将没有 assign 的数据从工作内存同步回主内存
  4. 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施 use、store 操作之前,必须经过 assign 和 load 操作
  5. 一个变量同一时间只有一个线程能对其进行 lock 。多次 lock 后,必须执行相同次数的 unlock 才能解锁
  6. 如果对一个变量进行 lock 操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新 load 或 assign 操作初始化变量的值
  7. 如果一个变量没有被 lock ,就不能对其进行 unlock 操作。也不能 unlock 一个被其他线程锁住的变量
  8. 对一个变量进行 unlock 操作之前,必须把此变量同步回主内存

      八种指令,图解如下图所示

       

       JMM 是一个规范,用来解决多线程通过共享内存进行通信时,存在本地内存数据不一致性的问题。本文JMM 部分就简单介绍到这里,如需继续深入了解,可自行百度。

解决方式

  Java 提供了三种保证有序性的方式,具体如下:

  • 使用 volatile 关键字保证有序性;
  • 使用 synchronized 关键字来保证有序性;
  • 使用显示锁 Lock 来保证有序性。

  1.volatile

      volatile 可以认为是一个轻量级的锁,

发布了247 篇原创文章 · 获赞 44 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/lzb348110175/article/details/103626737