IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java 内存模型 -> 正文阅读

[Java知识库]Java 内存模型

为了更好的理解 Java 是如何实现 按需禁用缓存和编译优化 的,我们首先需要对 Java 的内存模型有一个初步的了解。

Java 内存模型主要由以下三部分构成:1 个主内存、n 个线程、n 个工作内存(与线程一一对应),数据就在它们三者之间来回倒腾。那么怎么倒腾呢?靠的是 Java 提供给我们的 8 个原子操作:lockunlockreadloaduseassignstorewrite,其操作流程示意图如下:

在这里插入图片描述
一个变量从主内存拷贝到工作内存,再从工作内存同步回主内存的流程为:

|主内存| -> read -> load -> |工作内存| -> use -> |Java线程| -> assign -> |工作内存| -> store -> write -> |主内存|

Java 内存模型中的 8 个原子操作

  • lock:作用于主内存,把一个变量标识为一个线程独占状态。
  • read:作用于主内存,把一个变量的值从主内存传输到线程工作内存中,供之后的 load 操作使用。
  • load:作用于工作内存,把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use:作用于工作内存,把工作内存中的一个变量传递给执行引擎,虚拟机遇到使用变量值的字节码指令时会执行。
  • assign:作用于工作内存,把一个从执行引擎得到的值赋给工作内存的变量,虚拟机遇到给变量赋值的字节码指令时会执行。
  • store:作用于工作内存,把工作内存中的一个变量传送到主内存中,供之后的 write 操作使用。
  • write:作用于主内存,把 store 操作从工作内存中得到的变量值存入主内存的变量中。
  • unlock:作用于主内存,释放一个处于锁定状态的变量。

8 个原子操作的执行规则

有关变量拷贝过程的规则

  • 不允许 readloadstorewrite 单独出现
  • 不允许线程丢弃它最近的 assign 操作,即工作内存变化之后必须把该变化同步回主内存中
  • 不允许一个线程在没有 assign 的情况下将工作内存同步回主内存中,也就是说,只有虚拟机遇到变量赋值的字节码时才会将工作内存同步回主内存
  • 新的变量只能从主内存中诞生,即不能在工作内存中使用未被 loadassign 的变量,一个变量在 usestore 前一定先经过了 loadassign

有关加锁的规则

  • 一个变量在同一时刻只允许一个线程对其进行 lock 操作,但是可以被一个线程多次 lock(锁的可重入)
  • 对一个变量进行 lock 操作会清空这个变量在工作内存中的值,然后在执行引擎使用这个变量时,需要通过 assignload 重新对这个变量进行初始化
  • 对一个变量执行 unlock 前,必须将该变量同步回主内存中,即执行 storewrite 操作
  • 一个变量没有被 lock,就不能被 unlock,也不能去 unlock一个被其他线程 lock 的变量

可见性问题 -> 有序性问题

通过上图可以发现,Java 线程只能操作自己的工作内存,其对变量的所有操作(读取、赋值等)都必须在工作内存中进行,不能直接读写主内存中的变量。这就有可能会导致可见性问题:

  • 因为对于主内存中的变量 A,其在不同的线程的工作内存中可能存在不同的副本 A1、A2、A3。
  • 不同线程的 readloadstorewrite 不一定是连续执行的,中间可以插入其他命令。Java 只能保证 readloadstorewrite 的执行对于一个线程而言是连续的,但是并不保证不同线程的 readloadstorewrite 的执行是连续的,如下图:

    假设有两个线程 A 和 B,其中线程 A 在写入共享变量,线程 B 要读取共享变量,我们想让线程 A 先完成写入,线程 B 再完成读取。此时即便我们是按照 “线程 A 写入 -> 线程 B 读取” 的顺序开始执行的,真实的执行顺序也可能是这样的:storeA -> readB -> writeA -> loadB,这将导致线程 B 读取的是变量的旧值,而非线程 A 修改过的新值。也就是说,线程 A 修改变量的执行先于线程 B 操作了,但这个操作对于线程 B 而言依旧是不可见的
    那么如何解决这个问题呢?通过上述的分析可以发现,可见性问题的本身,也是由于不同线程之间的执行顺序得不到保证导致的,因此我们也可以将它的解决和有序性合并,即对 Java 一些指令的操作顺序进行限制,这样既保证了有序性,有解决了可见性。

于是乎,Java 给出了一些命令执行的顺序规范,也就是大名鼎鼎 Happens-Before 规则。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:09:58  更:2022-03-30 18:14:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:52:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码