【JVM】JMM 内存模型

JMM

概述

内存模型

  1. java[内存模型](Java Memory Model) 和 [内存结构]
  2. JMM规定了在多线程下对共享数据的读写时,对数据的原子性 有序性 可见性的规则和保障。

原子性

  1. 原子性问题:

在这里插入图片描述

i++和i–不是原子性操作! 所以一个i++指令会在执行过程中被另一个线程执行!
在这里插入图片描述

  1. 问题分析:

在这里插入图片描述

(1)共享的变量信息是放在主内存中的,线程呢是在工作内存中
多线程情况下指令交错产生的问题:
在这里插入图片描述

  1. 问题解决
    java中通过synchronized来保证了原子性

在这里插入图片描述

EntryList: 排队等候区:阻塞住

Owner: 有一个一个线程可以进入成为owner

monitorExit: 表示owner中线程执行完毕,会通知EntryList中等待的线程。然后就可以拥有抢夺owner的机会,当等待的线程成为owner后就会执行monitorenter 锁住monitor区域

WaitSet: 是线程wait() notify()的时候需要用到的区域

在这里插入图片描述

优化:
在这里插入图片描述

Java锁机制

64位开启指针压缩为例:

在这里插入图片描述

  1. java中锁的实现:每个对象都拥有一把锁,该锁存放在对象头中。锁中记录了当前对象被哪个线程所占用。

在这里插入图片描述

在这里插入图片描述

  1. 对象的结构

    1. 对象头:(存放了一些对象本身的运行时信息)
      1. Mark Word:存储了很多和当前对象运行时状态有关的数据。
      2. Class Pointer: 指向了堆中当前类class对象。
    2. 实例数据:属性 + 方法
    3. 对齐填充字节: 为了满足对象的大小为8个字节的倍数,无实际意义。
  2. 锁状态:对象头的Mark Word中"锁标志位"分别对应四种锁状态:无锁、偏向锁、轻量级锁、重量级锁

  3. synchronized(重量级锁)实现线程同步:

    • 通过生成的monitorenter和monitorexit来实现同步。结合monitor区域来实现线程同步。

synchronized 缺点:本质:通过线程状态的切换。用户态<–>内核态,所以很慢。

synchronized 编译后是通过jvm指令monitorenter和monitorexit来实现同步,而该jvm指令实际上依赖操作系统的mutex lock来实现的。java线程实际上是对操作系统线程的映射,所以每当挂起/唤醒一个线程,都要从"用户态"切换。操作系统的"内核态",这种操作是比较重量级的!一些情况下甚至切换时间本身会超过线程执行任务的时间!所以使用synchronized会对性能产生严重的影响!

jdk 6后,对synchronized进行了优化,引入了无锁、偏向锁、轻量级锁

  • 对应了对象头Mark Word中的四种状态[无锁 偏向锁 轻量级锁 重量级锁]

注意锁只能升级不能降级!

synchronized 优化

synchronized是如何优化的?四种状态如何变化的?

  1. 无锁:线程都可以来访问该资源

    1. 无竞争
    2. 存在竞争.可以通过非锁方式,同步线程。失败重试(乐观锁思想)
      • CAS: Compare And Swap:CAS在操作系统中通过一条指令来实现,所以能够保证原子性!
        且在标价和交换的过程中,必须是原子性。不然两个线程就会抢到一个资源!
      • cas和锁的区别:
        • 锁: 通过锁定资源的方式,保证线程同步。给对象加锁。
        • cas: 通过别的方式,不锁定资源。
  2. 偏向锁:

    1. 对象能够认识这个线程,这个线程来了直接把锁交出去,认为对象偏向这个线程。
    2. 在偏向锁状态时通过线程ID来确定当前想要获得对象锁的线程。
    3. 当有多个线程来竞争这把锁,偏向锁会升级为轻量级锁。
    4. 使用场景:只有一个线程来获取资源,单线程。
  3. 轻量级锁:这时线程会在虚拟机栈中开辟一块被称为Lock Record的空间,线程通过cas获取锁,一旦获取到锁,会复制该对象的mark Word,并且将Lock Record中的owner指针指向该对象,该对象的对象头的Mark Word的前30个bit也会指向栈中锁记录的指针,这就实现了对象与线程的绑定。

在这里插入图片描述

这时候也有其他线程想要获取该对象该怎么办?

  • 自旋等待:线程自己在不断的轮询循环,尝试着看一下目标对象的锁有没有被释放。
  • 这种方式区别于被操作系统挂起,因为对象的锁很快被释放的话,自旋就不需要系统中断和现场恢复。所以效率高一些。
  • 自旋相当于cpu在空转,如果时间过长会占用资源。
  • 如果自旋等待的线程数超过1个,那么轻量级锁会升级为重量级锁。
  1. 如果对象头中锁状态被标记为重量级锁,那么需要通过monitor来对线程进行控制。

    此时将会完全锁定资源,此时的管控也最为严格.

在这里插入图片描述

轻量级锁和重量级锁的区别:

  1. 重量级锁:通过monitor来控制线程同步,等待的线程会进入阻塞状态。涉及用户态到内核态的转变,这属于比较重量级的操作。
  2. 轻量级锁:通过等待的线程进行自旋的方式进行等待,不会阻塞。

CAS

  1. 不锁定资源,也能同步线程
  2. 预期原值与新值
    • 预期原值: 之前读到的资源对象的状态值
    • 新值: 想要将预期原值更新后的值
    • 内存位置: 资源本身当前的值(资源的状态值)
  3. 比较
    • 比较预期原值和内存位置(资源的状态值),一致的话将内存位置改为新值。这样别的线程的预期原值和内存位置就不一样了,就不操作了。
  4. 核心问题
    • "比较数值,并进行值的更新"这个操作必须是原子的!不然会出问题!!会有两个线程同时抢到…同时只能有一条线程进行操作!!!
  5. 如何实现CAS的原子性?
    • CAS在操作系统中就是一条指令,所以是原子性的。

CAS: 乐观锁。本质是一种无锁的同步机制。Java底层是通过Unsafe的方法实现CAS操作。

可见性

volatile: 一个线程写,多个线程读的场景,无法保证原子性。

一. 可见性问题

在这里插入图片描述

二. 可见性问题分析

线程t1对于t2修改后的值不清楚的原因是,t1线程频繁的读取boolean这个变量。然后即时编译器就会视为热点代码。将boolean的值缓存到高速缓存中。所以t1每次读取都是从自己的工作内存中读取。主内存中改了值,其实t1线程是感知不到的。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

synchronized 和 volatile 比较

  • synchronized 关键字用于给代码块上锁,volatile用于修饰成员变量/静态变量
  • synchronized 既可以保证原子性,也可以保证可见性。但是属于重量级的操作,是一种重量级锁,线程的用户态->内核态的转变。
  • volatile可以保证可见性和有序性,但是不能保证原子性。通过读写屏障保证线程之间的
    可见性和禁止编译器/处理器对指令进行指令重排序!

有序性

volatile保证了禁止指令重排序。

有序性的理解

  1. 单线程情况下jvm对其指令重排是不太影响结果的。(指令重排是一种优化)
  2. 多线程下指令重排就会产生一些问题!

单例模式下双重锁机制的变量需要加volatile,否则会指令重排。使得返回的对象不完整

在这里插入图片描述

在这里插入图片描述

happens-before

规定了哪些写操作可以对其他线程的读操作可见,是可见性与有序性的一套规则:

在这里插入图片描述

在这里插入图片描述

CAS

一. 概述

在这里插入图片描述

CAS是与volatile配合使用的一项技术。体现的是一种乐观锁的思想,是一种无锁并发技术。

compareAndSwap:如果旧值和共享变量相同,则进行swap,把共享变量改为结果。

CAS适用场景

  1. 竞争不激烈,多核CPU的情况下。因为等待的线程并不是进入阻塞状态,而是一种在尝试尝试。其他线程也需要占用CPU资源。如果竞争激烈,会影响效率!
  2. 获取"主内存中的值时",为了保证变量的可见性,需要使用volatile来修饰!结合CAS和volatile可以实现无锁并发!
  3. synchronized的比较:
    • 该种并发技术并不会使等待的线程进入阻塞状态,而是通过不断尝试的方式来操作。但是当竞争激烈时,等待的线程会占用过多CPU资源,导致效率下降!
    • CAS操作底层依赖于一个Unsafe类直接调用操作系统底层的CAS指令。

二. 底层实现

CAS操作底层依赖于一个Unsafe类直接调用操作系统底层的CAS指令。

三. 原子操作类

  • java中的悲观锁:synchronized
  • java中的乐观锁:CAS

在这里插入图片描述

在这里插入图片描述

锁膨胀

在这里插入图片描述

自旋锁:自旋是一种在重量级锁上的优化,并不会让等待的线程进入阻塞状态,而是处于一种
自旋重试的操作!

在这里插入图片描述

其他优化

  1. 缩少锁的粒度:
    1. ConcurrentHashMap: 原来HashTable是锁住了整个数组,ConcurrentHashMap是给数组(每个链表头)进行加锁,也就是分段的机制,当前段的上锁,不影响其他段的读写操作!

JMM总结

在这里插入图片描述

  1. Java中锁的实现

每个对象都拥有一把锁,该锁存放在对象头中。锁中记录了当前对象被哪个线程所占用。

  1. 对象的结构
  • 对象头:存放了一些对象本身的运行时信息,包括两部分:
    • Mark Word: 存储了很多和当前对象运行时状态有关的数据。
    • Class Pointer: 指向了堆中当前类class对象。
  • 实例数据: 属性 + 方法
  • 对齐填充字节: 为了满足对象的大小为8个字节的倍数,无实际意义。
  1. 锁状态

对象头的Mark Word中"锁标志位"分别对应四种锁状态:

无锁01、偏向锁01、轻量级锁00、重量级锁10

  1. synchronized (重量级锁) 实现线程同步

通过生成的monitorentermonitorexit来实现同步。

synchronized的缺点:本质上是通过线程状态的切换,用户态<–>内核态,所以很慢。synchronized编译后是通过JVM指令monitorentermonitorexit来实现同步,而这些JVM指令实际上依赖操作系统的mutex lock来实现的。Java线程实际上是对操作系统线程的映射,所以每当挂起/唤醒一个线程,都要从"用户态"切换操作系统的"内核态",这种操作是比较重量级的!一些情况下甚至切换时间本身会超过线程执行任务的时间!所以使用synchronized会对性能产生严重的影响!

monitor区域…

在这里插入图片描述

从JDK 6后,对synchronized进行了优化,引入了无锁,偏向锁,轻量级锁。

注意,锁只能升级不能降级!

synchronized 优化

synchronized是如何优化的?四种状态如何变化的?

  1. 无锁:线程都可以来访问该资源

    1. 无竞争

    2. 存在竞争.可以通过非锁方式,同步线程。失败重试(乐观锁思想)

      • CAS: Compare And Swap:CAS在操作系统中通过一条指令来实现,所以能够保证原子性!
        且在标价和交换的过程中,必须是原子性。不然两个线程就会抢到一个资源!

      • cas和锁的区别:

        • 锁: 通过锁定资源的方式,保证线程同步。给对象加锁。
        • cas: 通过乐观锁思想方式,不锁定资源。
  2. 偏向锁

    • 对象能够认识这个线程,这个线程来了直接把锁交出去,认为对象偏向这个线程。称为偏向锁。
    • 在偏向锁状态时通过线程ID来确定当前想要获得对象锁的线程。
    • 当有多个线程来竞争这把锁,偏向锁会升级为轻量级锁。
    • 使用场景: 只有一个线程来获取资源。单线程。
  3. 轻量级锁在这里插入图片描述

    • 这时线程会在虚拟机栈中开辟一块被称为Lock Record的空间。
    • 线程通过CAS获取锁,一旦获取到锁,会复制该对象的Mark Word,并且将Lock Record中的owner指针指向该对象,该对象的对象头的Mark Word的前30个bit也会指向栈中锁记录的指针,这就实现了对象与线程的绑定。
    • 这时候也有其他线程想要获取该对象该怎么办?
      • 自旋等待: 线程自己在不断的轮询循环,尝试着看一下目标对象的锁有没有被释放。
      • 如果自旋等待的线程数超过1个,那么轻量级锁会升级为重量级锁。
      • 锁膨胀: 竞争的线程想要通过CAS操作获取共享对象的锁的时候,获取不到。就会进行锁膨胀,转化为重量级锁!
  4. 重量级锁

    • 如果对象头中锁状态被标记为重量级锁,那么需要通过monitor来对线程进行控制…
      • 此时将会完全锁定资源,此时的管控也最为严格。

轻量级锁和重量级锁的区别:

  1. 重量级锁:通过monitor来控制线程同步,等待的线程会进入阻塞状态。涉及用户态到内核态的转变,这属于比较重量级的操作。
  2. 轻量级锁:通过等待的线程进行自旋的方式进行等待,不会阻塞。

CAS

  1. 不锁定资源,也能同步线程
  2. 预期原值与新值
    • 预期原值: 之前读到的资源对象的状态值
    • 新值: 想要将预期原值更新后的值
    • 内存位置: 资源本身当前的值(资源的状态值)
  3. 比较
    • 比较预期原值和内存位置(资源的状态值),一致的话将内存位置改为新值。这样别的线程的预期原值和内存位置就不一样了,就不操作了。
  4. 核心问题
    • "比较数值,并进行值的更新"这个操作必须是原子的!不然会出问题!会有两个线程同时抢到。同时只能有一条线程进行操作!
  5. 如何实现CAS的原子性?
    • CAS在操作系统中就是一条指令,所以是原子性的。

CAS: 乐观锁。本质是一种无锁的同步机制 + volatile。Java底层是通过Unsafe的方法实现CAS操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1411214.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第67题二进制求和

题目&#xff1a; 题解&#xff1a; class Solution { public:string addBinary(string a, string b) {string ans;reverse(a.begin(), a.end());reverse(b.begin(), b.end());int n max(a.size(), b.size()), carry 0;for (size_t i 0; i < n; i) {carry i < a.siz…

最佳实践|Apifox 中通过 CryptoJS 给请求参数进行 AES 加密!

假如现在要在 Apifox 中发送一个「登录」的请求&#xff0c;然后我需要将接口中的 password 参数使用 AES 加密算法加密以后&#xff0c;再传给后台服务&#xff0c;这要怎么做&#xff1f; 要在 Apifox 中使用 AES 加密算法对 password 参数进行加密&#xff0c;你需要在「前置…

4 Spring AOP

目录 AOP 简介 传统开发模式 先来看一个需求 解决方案 AOP 图示 Spring 启用 AspectJ 基于 xml 配置 创建 pom.xml 创建 UserService 借口和 UserServiceImpl实现类 创建 LogAdvice 日志通知 创建 log4j.properties 重点&#xff1a;创建 spring-context-xml.xml 配…

如何让 PDF 书签从杂乱无序整洁到明丽清新

1、拉取书签&#xff08;详细步骤看文末扩展阅读&#xff09; 原状态 —— 杂乱无序 自动整理后的状态 —— 错落有致&#xff0c;但摩肩接踵 2、开始整理 全选自动整理后的书签&#xff0c;剪切 访问中英混排排版优化 - 油条工具箱 https://utils.fun/cn-en 1 粘贴 → 2 …

国科大模版修订模式冲突

定位到 \documentclass[twoside]{Style/ucasthesis}%前添加\PassOptionsToPackage{changes,usenames,dvipsnames,table}{xcolor} 完整如下&#xff1a; \PassOptionsToPackage{changes,usenames,dvipsnames,table}{xcolor} \documentclass[twoside]{Style/ucasthesis}% \us…

Sarcasm detection论文解析 |A2Text-Net:一种用于讽刺检测的新型深度神经网络

论文地址 论文地址&#xff1a;A2Text-Net: A Novel Deep Neural Network for Sarcasm Detection | IEEE Conference Publication | IEEE Xplore github:lliyuan1117/A2Text-Net (github.com) 论文首页 A2Text-Net&#xff1a;一种用于讽刺检测的新型深度神经网络 &#x1f4c5…

QT:label标签/进度条的使用

文章目录 设置不同格式的文本显示图片文本对齐/自动换行/缩进/边距LCDNumber倒计时 ProgressBar进度条 设置不同格式的文本 在文本格式中&#xff0c;存在富文本&#xff0c;makedown格式的文本&#xff0c;还有纯文本&#xff0c;下面就依据这三个进行举例 #include "w…

数据库基础--MySQL多表查询之外键约束

MySQL多表关系 一对一 顾名思义即一个对应一个的关系&#xff0c;例如身份证号对于每个人来说都是唯一的&#xff0c;即个人信息表与身份证号信息表是一对一的关系。车辆信息表与车牌信息表也是属于一对一的关系。 一对多 即一个表当中的一个字段信息&#xff0c;对应另一张…

MLP手写数字识别(1)-MNIST数据集下载与可视化(tensorflow)

1.下载与查看MNIST数据集 from keras.datasets import mnist(x_train_image,y_train_label),(x_test_image,y_test_label) mnist.load_data() print("train images:",x_train_image.shape) print("test images:",x_test_image.shape) print("train …

WAAP动态安全解决方案

随着企业数字化进程不断加速&#xff0c;应用安全面临多重威胁&#xff0c;新型攻击方式层出不穷&#xff0c;常见的攻击形式包括Web应用攻击、DDoS攻击、API攻击、恶意爬虫攻击等。企业正面临严峻的安全防护挑战&#xff0c;需寻找一个可靠、全面的安全解决方案。在此情况下&a…

基于Springboot的校园食堂订餐系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园食堂订餐系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

P9422 [蓝桥杯 2023 国 B] 合并数列

P9422 [蓝桥杯 2023 国 B] 合并数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 用队列即可 当两个队列队首&#xff1a;a b &#xff0c;弹出 当a < b&#xff0c;把a加给其后一个元素&#xff0c;弹出a 当b < a&#xff0c;把b加给其后一个元素&#xff0c;弹出…

亚马逊云科技AWS免费证书-EC2服务器设计(含题库)

亚马逊云AWS官方程序员专属免费证书又来了&#xff01;这次证书是关于AWS EC2实例的设计和搭建&#xff0c;EC2作为AWS服务的核心&#xff0c;是学好AWS的第一步。强推没有任何AWS背景和转码的小伙伴去学&#xff01;学完也能变成AWS开发大神&#xff01; 证书名字叫Getting St…

C++---入门基础

一、命名空间 在C/C中&#xff0c;有大量的函数&#xff0c;变量乃至类&#xff0c;这些函数&#xff0c;变量和类的名称都将作用于全局作用域中&#xff0c;这可能会导致命名冲突。针对这个问题&#xff0c;我们就会使用命名空间&#xff0c;命名空间的目的就是对标识符及名称…

【ESP32之旅】合宙ESP32-C3 使用PlatformIO编译和Debug调试

工程创建 首先打开PIO Home窗口&#xff0c;然后点击New Project来创建新的工程&#xff0c;工程配置选择如下图所示&#xff1a; 注&#xff1a; 选择板子型号的时候需要选择ESP32C3&#xff0c;勾选取消Location可以自定义路径。 修改配置文件 工程创建完毕之后在工程根…

菜鸡学习netty源码(三)—— Reactor 模型

1.概述 我们先进行理解一下Reactor模型&#xff0c;知道什么是Reactor模型&#xff0c;它有什么特别之处。我们先来简单介绍一下这个Reactor模型。 Reactor模型的核心思想&#xff1a; 就是将所关注的I/O事件进行注册到一个多路复用器上&#xff0c;一旦有I/O事件的发生&#…

Redis---------实现商品秒杀业务,包括唯一ID,超卖问题,分布式锁

订单ID必须是唯一 唯一ID构成&#xff1a; 代码生成唯一ID&#xff1a; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.tim…

云商城系统,无后门,一站式系统Java源码

云商城系统&#xff0c;无后门&#xff0c;一站式系统Java源码&#xff0c;心权益商品数量不限数量 系统对接 手动发货 自动发货 兑 换 码 订单监控 商品监控 对象存储 邮箱提醒 加价模板 密价功能 三方支付 会员体系 财务明细 交易分析 售后服务 技术支持 建议配置&#xf…

使用PyTorch从头实现Transformer

前言 本文使用Pytorch从头实现Transformer&#xff0c;原论文Attention is all you need paper&#xff0c;最佳解读博客&#xff0c;学习视频GitHub项目地址Some-Paper-CN。本项目是译者在学习长时间序列预测、CV、NLP和机器学习过程中精读的一些论文&#xff0c;并对其进行了…

BUUCTF---misc---被偷走的文件

1、题目描述 2、下载附件&#xff0c;是一个流量包&#xff0c;拿去wireshark分析&#xff0c;依次点开流量&#xff0c;发现有个流量的内容显示flag.rar 3、接着在kali中分离出压缩包&#xff0c;使用下面命令&#xff0c;将压缩包&#xff0c;分离出放在out3文件夹中 4、在文…