首页 > 其他分享 >简单了解JMM

简单了解JMM

时间:2024-03-24 20:00:02浏览次数:28  
标签:变量 JMM 了解 volatile 内存 简单 线程 操作

什么是JMM

对于不同的硬件和操作系统,有着自己的底层内存模型,可能导致Java程序在一些的平台可以正确并发,而在另一些平台出现并发错误,JMM是Java内存模型,是语言级别的内存模型,用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM主要考虑的就是各种变量的访问规则,正确实现线程间的通信以及线程间的同步,对于JMM,把各种硬件抽象为本地内存和主存来存放变量。

总的来说JMM把硬件和操作系统的差异屏蔽了,让Java在不同机器上都可以正确的实现并发。

image-20240318203150822

内存间交互操作

关于变量如何从主存拷贝到本地线程以及变量如何从本地线程同步到主存的细节,JMM定义了8中操作来完成,对于每个线程来说,只能操作本地内存的变量,规定read、load要顺序执行,store、write要顺序执行。

image-20240318205713894

操作系统的三大问题

在谈论JMM之前,首先我们需要了解操作系统的几个问题,JMM正是解决了操作系统这些问题。

随着时代的发展,计算机逐步更新换代,人们对于计算机的要求越来越高,对于家庭或者公司的电脑需要人机交互,这就要求计算机需要有较短的响应时间,为此工程师引入了CPU的多级缓存、时间片轮转调度算法、指令优化重排,但是也相应的带来了问题:

  • CPU缓存一致性问题(可见性),解决方案:总线锁、MESI协议

  • 代码被多个线程访问的并发问题(原子性),解决方案:锁

  • 指令没有按照程序员的意愿执行(有序性)

JMM抽象的问题

可见性:线程本地变量和内存的可见性问题,规定线程只能操作本地内存变量,线程对内存的修改无法及时被其它线程看到

原子性:如 i++ 语句,底层由多条指令组成,CPU实际执行的是指令,在指令执行期间可能会发生时钟中断切换线程,最终导致程序执行结果出问题

有序性:如 Java 创建对象过程,类加载 -> 实例化 -> 初始化 -> 引用赋值,由于指令重排可能导致引用赋值在初始化之前,导致其它线程使用到未初始化的对象

Java关键字如何解决问题

  • 可见性:

    • 可通过volatile关键字解决,强制线程每次修改本地变量需要同步到主存,同时每次读取本地变量都需要从主存读取

    • 可通过synchrinized关键字解决:锁获取和volatile读有相同的语义,将本地变量置为无效状态,锁释放和volatile写有相同的语义,变量需要同步到主存

    • 可通过final关键字解决

  • 原子性:可通过synchrinized关键字解决,底层是通过monitorenter和monitorexit字节码指令

  • 有序性:

    • 可通过volatile关键字解决,volatile规则

    • 可通过synchrinized关键字解决,程序次序规则(只有一个线程访问同步代码块,单线程重排优化语义是一样的)

Happens-Before原则

如果JMM中所有的有序性仅靠volatile和synchronized来完成,未免很多操作有点啰嗦,我们在日常编码的过程中并不会过多关注有序性,因为有个Happens-Before原则来辅助保证代码操作的顺序性

常见的Happens-Before原则:

  • 程序次序规则:同一个线程内,对于顺序执行,前面的操作先行于后面的操作

  • 管程锁定规则:对于一个锁,unlock先行于lock

  • volatile规则:对于一个用volatile修饰的变量,写操作先行于读操作

  • 线程启动规则:start()先行于这个线程的其它动作

  • 线程终止规则:线程所有动作先行于终止检查

  • 线程中断规则:interrupt()先行于检测到中断事件的发生

  • 对象终止规则:对象的初始化完成先行于销毁

  • 传递性:A操作先行于B操作,B操作先行于C操作,那么A操作先行于C操作

如下面这个程序,你不能保证 i = 1 一定会在 j = 2 前执行,但是这也不影响Happens-Before原则的正确性,因为这并不影响整个程序执行结果的正确性。

 int i = 1;
 int j = 2;

标签:变量,JMM,了解,volatile,内存,简单,线程,操作
From: https://blog.csdn.net/xtrans/article/details/136822357

相关文章

  • day9 嵌套排序,利用嵌套循环所写的简单时钟
    #define_CRT_SECURE_NO_WARNINGS#include<stdio.h>//利用嵌套循环编写一个时间的函数#include<Windows.h>//利用windows的函数来定义时刻。intmain(){   for(inti=0;i<24;i++)//嵌套的本质是外层执行一次内层执行一周,内层如果再嵌套则此时的内层表示的是......
  • C语言-扫雷游戏的简单实现
    文章目录扫雷游戏的简单实现1.初始化棋盘2.打印棋盘3.在棋盘中布置雷4.排查雷扫雷游戏的简单实现本篇博客采用了多文件的方式来实现扫雷游戏geme.h----------函数的声明及符号的定义game.c-----------函数的实现test.c-----------游戏的运行主体代码如下g......
  • 深入了解:二叉树(最详细,约10000字)
    目录1.树概念及结构1.1树概念1.2树的表示2.二叉树概念及结构2.1概念2.2数据结构中的二叉树2.3特殊的二叉树2.4二叉树的存储结构2.4.1顺序存储2.4.2链式存储2.5二叉树的性质3.二叉树顺序结构及概念3.1二叉树的顺序结构3.2堆的概念及结构3.3堆的实现4.二叉树......
  • 知识总结--简单复习各部件
    目录内部结构部件介绍配置步骤之前学了很多部件,配置了很多参数,但是没有很系统地把他们连接在一起,今天这个图里简洁描述了资源与资源之间的关系。内部结构部件介绍黑框部分为CPU、内部有一个内核专门处理事件,所有的电信号中断信号都由内核处理。红框:CPU与外界用引脚......
  • 简单几步为APP搭建移动直播功能——智密腾讯云直播组件简介
    随着5G移动网络技术的成熟,视频直播已经不再局限于WIFI 环境下。即使在户外,当前的4G和5G信号覆盖也足够支持用户观看直播,并在直播间进行留言、连线、点赞和赠送礼物。因此添加直播功能就是现在很多已上线的APP中比较广泛的需求。在目前的支持直播的技术提供商中,腾讯云已经有了比......
  • 编写简单的nginx Dockerfile文件
    .1.创建Dockerfile文件touchDockerfile添加如下相关的配置信息 #设置维护者信息LABELmaintainer="[email protected]" #使用官方Nginx基础镜像FROMnginx:1.24.0 #安装构建Nginx模块所需的工具和依赖RUNapkadd--no-cache--virtual.build-deps\   ......
  • 使用etcd来实现一个简单的分布式锁
    使用etcd来实现一个简单的分布式锁使用etcd来实现一个简单的分布式锁分布式锁有着极为广泛的使用,在多节点服务部署中是必不可少的一环.在本文中,我们尝试以etcd为基础来实现一个简单的分布式锁.基本能力Lock上锁Unlock解锁一些额外的设置,比如Watch-Dog模式/设置最......
  • 使用etcd来实现一个简单的分布式锁
    使用etcd来实现一个简单的分布式锁使用etcd来实现一个简单的分布式锁分布式锁有着极为广泛的使用,在多节点服务部署中是必不可少的一环.在本文中,我们尝试以etcd为基础来实现一个简单的分布式锁.基本能力Lock上锁Unlock解锁一些额外的设置,比如Watch-Dog模式/设置最......
  • Linux操作系统的简单终端(Terminal)命令
    ls:列出当前目录下的文件和文件夹。你可以使用ls-l来查看详细信息,或者使用ls-a来显示包括隐藏文件在内的所有文件。cd:用于切换目录。例如,cdDocuments会进入Documents文件夹。你还可以使用cd..来返回上一级目录,或者使用cd~来回到你的主目录。pwd:显示当前所在的目录路径。chm......
  • 最快的 Python API 框架之一:简单、现代、高性能 | 开源日报 No.207
    tiangolo/fastapiStars:68.1kLicense:MITfastapi是一个现代、高性能、易学习、快速编码且适用于生产环境的框架。其主要功能和核心优势包括:高性能:与NodeJS和Go相当,是最快的Python框架之一。编码速度快:开发特性的速度提高约200%到300%。减少错误:减少大约......