可见性

来源:互联网 发布:mac 删除桌面安装程序 编辑:程序博客网 时间:2024/04/29 11:29

Java内存模型

Java内存模型把Java虚拟机内部划分为线程栈和堆。每个线程都有自己的线程栈。如下图。

  • 线程栈(Thread Stack):线程栈中存放了本地变量(线程执行过程中在方法内定义的变量,无论是基本类型还是引用类型)
  • 堆(Heap):堆上则存放Java程序中创建的所有对象,一个对象包含的成员变量跟随对象一起存放在堆上

    这里写图片描述

硬件内存架构

在多核CPU中,在同一时刻,可能每个CPU上边都同时分别跑着一个线程。

CPU缓存

  • 寄存器:每个CPU都包含一系列的寄存器,它们是CPU内内存的基础(寄存器容量小,但访问速度远远大于内存)。
  • 缓存层:每个CPU可能还有一个CPU缓存层。实际上,绝大多数的现代CPU都有一定大小的缓存层。CPU访问缓存层的速度快于访问主存的速度,但通常比访问内部寄存器的速度还要慢一点。一些CPU还有多层缓存。

CPU读写与缓存的关系

  • 读:当一个CPU需要读取主存时,它会将主存的部分读到CPU缓存中。它甚至可能将缓存中的部分内容读到它的内部寄存器中,然后在寄存器中执行操作。
  • 写:当CPU需要将结果写回到主存中去时,它会将内部寄存器的值刷新到缓存中,然后在某个时间点将值刷新回主存。

对于理解Java内存模型和内存交互原理,只需要知道CPU内部有一个缓存即可。如下图。

这里写图片描述

Java内存模型和硬件内存架构之间的桥接

硬件内存架构没有区分线程栈和堆。对于硬件,所有的线程栈和堆都分布在主内中。部分线程栈和堆可能有时候会出现在CPU缓存中和CPU内部的寄存器中。如下图。

这里写图片描述

产生问题

当对象和变量被存放在计算机中各种不同的内存区域中时,就可能会出现一些具体的问题。主要包括如下两个方面:

  • 线程对共享变量修改的可见性
  • 当读,写和检查共享变量时出现竞争条件(race conditions)

共享对象可见性

对于共享对象,多个CPU上的线程可以同时将其读到CPU缓存中,然后修改这个对象。只要CPU缓存没有被刷新会主存,对象修改后的版本对跑在其它CPU上的线程都是不可见的。这种方式可能导致每个线程拥有这个共享对象的私有拷贝,每个拷贝停留在不同的CPU缓存中。如下图。
从本质上来讲,这是CPU的自身速度优化,使最新的Java对象不能及时地在多个线程相互可见(一个特性的优化往往会对另一个特性造成缺口)

这里写图片描述

竞争条件

如果两个线程同时将整型变量count读到各自的CPU缓存中,并且同时对其执行+1操作,最后再将count写会到内存中,最终count的值只会被+1,而不是+2,尽管执行了两次+1操作。
这里写图片描述


原文链接

0 0
原创粉丝点击