Java虚拟机——Java内存模型

  • "Java内存模型"来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
  • 主要目的:定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节
  • 这里的变量包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数(它们是线程私有的)

1 主内存与工作内存

  • Java内存模型规定了 所有的变量都存储主内存
  • 每条线程还有自己的工作内存,工作内存保存了被该线程使用的变量的主内存副本
    1. 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的数据。
    2. 不同的线程之间还无法访问对方工作内存中的变量
    3. 线程间变量值的传递均需要通过主内存来完成。
      在这里插入图片描述
      在这里插入图片描述

2 内存间交互操作

  • 主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存这一类的实现细节。
  • Java内存模型定义了以下8种操作符来实现。且必须保证下面提及的每一种操作都是原子的、不可再分的。
    在这里插入图片描述
  • 八种操作规则
    在这里插入图片描述

3 volatile型变量的特殊规则

  • 关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制
  • 当一个变量被定义成volatile之后,它将具备两种特性
  1. 第一项是保证此变量对所有线程的可见性(可见性指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立刻的得知的)
  • 但是不代表volatile变量对所有线程是立刻可见的,对volatile变量的所有写操作都能立刻反映到其他线程之中。
  • Java里面的运算操作符不是原子操作,这可能会导致volatile变量的运算在并发下一样是不安全的。
public class VolatileTest {

    public static volatile int race = 0 ;

    public static void increase(){
        race++;
    }

    private static final int THREADS_COUNT = 20;

    public static void main(String[] args) {
        Thread[] threads = new Thread[THREADS_COUNT];
        for(int i = 0 ; i < THREADS_COUNT ; i++){
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 0 ; i < 10000 ; i++){
                        increase();
                        System.out.println(race);
                    }
                }
            });
            //开启当前子线程
            threads[i].start();
        }

        //等待所有累加线程都结束
        while (Thread.activeCount() > 1){
            Thread.yield();
        }

        System.out.println(race);
    }
}

在这里插入图片描述

  1. 禁止指令重排序优化,普通的变量仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序 与 程序代码中的执行顺序一致。
  • 正常情况下,执行顺序是1 2 3 4
  • 但是在多线程环境下,执行顺序可能是 2 1 3 4 或者 1 3 2 4 。 这就是指令的重排,也就是内部执行顺序和代码顺序不同。
  • 但是指令重排还是有限制的,不会出现下面的顺序, 4 3 2 1。因为要考虑到指令之间的数据依赖性。
//这是指令重排序的案例
public void mySort(){
    int x = 11;
    int y = 12;
    x = x + 5;
    y = x * x;
}

Volatile针对指令重排做了啥?

  • 禁止了指令重排优化,从而避免了多线程环境下程序出现乱序执行的现象
  • 首先需要了解内存屏障的概念,它是一个CPU指令。它的作用有两个
  1. 保证特定操作的顺序
  2. 保证某些变量的内存可见性
  • 由于编译器和处理器都能执行指令重排的优化,所以如果在指令间插入内存屏障会告诉编译器和CPU,什么指令都不能和这条内存屏障重排序。 通过插入内存屏障进制内存屏障前后的指令执行重排序优化。

4 针对long和double型变量的特殊规则

  • Java内存模型要求lock、unlock、read、load、assign、use、store和write这八种操作具有原子性,但是对于64位的数据类型long和double来说,定义了一条宽松的规定:允许虚拟机将没有被volatile修饰的64位数据类型的读写操作划分为两次32位的操作来进行。
  • **即允许虚拟机实现自行选择是否要保证64位数据类型的load、store、read和write这四个操作的原子性。**这就是long和double1的非原子性协定。

如果有多个线程共享一个为声明为volatile的long或double类型变量,并且同时对他们进行读取和修改操作,那么某些线程可能得到一个既不是原值,也不是修改后的值。

5 原子性、可见性与有序性

原子性

  • 根据原子性变量操作(read…)可以大致认为:基本数据类型的访问、读写都是具有原子性的

可见性

  • 可见性就是当一个线程修改了共享变量的值时,其他线程能够立刻得知这个修改。
  • Java内存,模型是通过在变量修改后将新值同步回主内存、在变量读取前从主内存中刷新变量值来实现的可见性。
  • volatile相比普通变量的区别在于 它保证了新值可以立刻同步到主内存。

有序性

  • 如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。Java语言提供了volatile和synchronized来保证线程之间操作的有序性。

6 先行发生原则

  • Java中有一个"先行发生"的原则,它可以判断数据是否存在竞争、线程是否安全的非常有用的手段。Java内存模型中存在一些"天然的"先行发生关系,可以在编码中直接使用。
  • 如果两个操作之间的关系不在此列,并且无法从下列规则中推导出来,说明它们没有顺序性的保障,虚拟机可以随意对它们进行重排序。
  1. 程序次序规则:在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。(不是程序代码顺序,需要考虑分支、循环等结构)。
  2. 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
  3. volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。
  4. 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
  5. 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread::join()方法是否结束、Thread::isAlive()的返回值等手段检测线程是否已经终止执行。
  6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread::interrupted()方法检测到是否有中断发生。
  7. 对象终结规则:一个对象的初始化完成先行发生于它的finalize()方法的开始。
  8. 传递性:如果操作A先行发生于操作B,操作B先行发生于操作A,那么可以得出A先行C

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用 Node.js 手写 WebSocket 协议

目录 引言 从 http 到 websocekt 的切换 Sec-WebSocket-Key 与 Sec-WebSocket-Accept 全新的二进制协议 自己实现一个 websocket 服务器 按照协议格式解析收到的Buffer 取出opcode 取出MASK与payload长度 根据mask key读取数据 根据类型处理数据 frame 帧 数据的发…

使用Python搭建代理服务器- 爬虫代理服务器详细指南

搭建一个Python爬虫代理服务器可以让你更方便地管理和使用代理IP。下面是一个详细的教程来帮助你搭建一个简单的Python爬虫代理服务器&#xff1a; 1. 首先&#xff0c;确保你已经安装了Python。你可以在官方网站(https://www.python.org/)下载并安装最新版本的Python。 2. 安…

一.MySQL的主从复制

目录 一.MySQL的主从复制 1.2主从复制的工作过程和原理 1.2.1主从复制工作过程为两日志和三线程 ​编辑 1.2.2主从复制的工作原理 1.3主从复制延迟的原因 1.4主从复制的架构 1.5.MySQL四种同步方式 1.5.1异步复制(Async Replication) 1.5.2 同步复制&#xff08;Sync Re…

新功能 – Cloud WAN:托管 WAN 服务

我很高兴地宣布&#xff0c;我们推出了 Amazon Cloud WAN&#xff0c;这是一项新的网络服务&#xff0c;它可以轻松构建和运营连接您的数据中心和分支机构以及多个 Amazon 区域中的多个 VPC 的广域网&#xff08;WAN&#xff09;。 亚马逊云科技开发者社区为开发者们提供全球的…

Linux centos7.x系统 下没有ens33 网卡的解决方案

一、背景 安装完windows11 Centos7.9 版本的双系统之后 , 启动Centos7.9时发现没有网卡信息 , 只有ifcfg-lo网卡的信息 , 这个时候就证明没有网卡信息&#xff0c;或者网卡驱动不匹配(我这里是没有网卡)&#xff0c;所以我们要重新安装 , 安装步骤如下 : 二、安装步骤 1.查…

使用分布式HTTP代理爬虫实现数据抓取与分析的案例研究

在当今信息爆炸的时代&#xff0c;数据已经成为企业决策和发展的核心资源。然而&#xff0c;要获取大规模的数据并进行有效的分析是一项艰巨的任务。为了解决这一难题&#xff0c;我们进行了一项案例研究&#xff0c;通过使用分布式HTTP代理爬虫&#xff0c;实现数据抓取与分析…

C++多线程编程(第二章 多线程通信和同步)

1、多线程状态 1.1线程状态说明 初始化&#xff08;Init&#xff09;:该线程正在被创建&#xff1b; 就绪&#xff08;Ready&#xff09;:该线程在就绪列表中&#xff0c;等待CPU调度&#xff1b; 运行&#xff08;Running&#xff09;:该线程正在运行&#xff1b; 阻塞&…

吉林大学计算机软件考研经验贴

文章目录 简介政治英语数学专业课 简介 本人23考研&#xff0c;一战上岸吉林大学软件工程专硕&#xff0c;政治72分&#xff0c;英一71分&#xff0c;数二144分&#xff0c;专业课967综合146分&#xff0c;总分433分&#xff0c;上图&#xff1a; 如果学弟学妹需要专业课资料…

Pytorch个人学习记录总结 07

目录 神经网络-非线性激活 神经网络-线形层及其他层介绍 神经网络-非线性激活 官方文档地址&#xff1a;torch.nn — PyTorch 2.0 documentation 常用的&#xff1a;Sigmoid、ReLU、LeakyReLU等。 作用&#xff1a;为模型引入非线性特征&#xff0c;这样才能在训练过程中…

leetcode 852. Peak Index in a Mountain Array(峰值索引)

一个数组保证是峰值数组&#xff08;存在一个值大于左边和右边部分数组&#xff09;&#xff0c;找出峰值的index。 要求时间复杂度在O(logn)。 思路&#xff1a; 时间复杂度为O(logn), 可以想到用binary search. 其实用O(n)的找最大值也能通过。 public int peakIndexInMou…

Spring Boot 拦截器实现:登录验证 统一异常处理 返回数据规范化

学习 Spring 和 servlet 初期&#xff0c;我们在判断用户身份时&#xff0c;都是在每个方法中获取会话、获取对象&#xff0c;这种方式冗余度高&#xff0c;增加代码复杂度&#xff0c;维护成本也高&#xff0c;因此想到可以使用 AOP 来实现一个公共的方法&#xff0c;这个公共…

android逆向环境下载记录

frida、frida_tools、obejction、wallbreaker https://github.com/frida/frida/releases pip install frida14.1.2 pip install frida-tools9.0.1 pip install objection1.9.6 https://github.com/hluwa/Wallbreaker objection -g com.hexin.plat.android explore -P ~/.objec…

JAVA基础-基于多线程的聊天程序

引言 什么是程序 &#xff1f; 一个程序可以有多个进程 。程序是一段静态的代码&#xff0c;它是应用程序执行的蓝本。 什么是进程 &#xff1f; 一个进程可以有多线程 进程是指一种正在运行的程序&#xff0c;有自己的地址空间。 作为蓝本的程序可以被多次加载到系统的不同内…

智能也是一切社会关系的总和

马克思把人作为“一切社会关系的总和”的论述中&#xff0c;他并非将自然条件作为固定的被给予的条件&#xff0c;而是作为在历史进程中&#xff0c;由于人的活动而发生的改变的被给予的条件来把握的&#xff0c;既从一开始就已经被一定的“生产关系”所塑形和中介了。智能&…

计算机启动过程uefi+gpt方式

启动过程&#xff1a; 一、通电 按下开关&#xff0c;不用多说 二、uefi阶段 通电后&#xff0c;cpu第一条指令是执行uefi固件代码。 uefi固件代码固化在主板上的rom中。 &#xff08;一&#xff09;uefi介绍 UEFI&#xff0c;全称Unified Extensible Firmware Interface&am…

Upload-Labs通关

目录 问题 我们首先先来了解一下什么是文件上传 一句话木马 web是用什么语言开发的 最简单的一句话木马 解释 了解完一句话木马 我们了解一下 蚁剑的工作原理 Pass-1 前端验证 1.通过浏览器的插件 关闭这个前端函数 2.通过bp来抓包修改后缀 Pass-2 文件类型的匹配 …

Flutter 状态组件 InheritedWidget

Flutter 状态组件 InheritedWidget 视频 前言 今天会讲下 inheritedWidget 组件&#xff0c;InheritedWidget 是 Flutter 中非常重要和强大的一种 Widget&#xff0c;它可以使 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget&#xff0c;从而简化了状态管理和数据传递…

高数笔记02:导数、微分、中值定理

图源&#xff1a;文心一言 本文是我学习高等数学第二、三章导数、微分、中值定理的一些笔记和心得&#xff0c;希望可以与考研路上的小伙伴一起努力上岸~~&#x1f95d;&#x1f95d; 第1版&#xff1a;查资料、画导图、归纳题型~&#x1f9e9;&#x1f9e9; 参考用书1&…

{“msg“:“invalid token“,“code“:401}

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; {“msg“:“invalid token“,“code“:401} 前端请求 后端接口时&#xff0c; 请求失败&#xff0c;控制台出现如下所示报错信息 问题描述 问题&#xff1a; 控制台报错信息如下所示&#xff1a; …

c语言内存函数的深度解析

本章对 memcpy&#xff0c;memmove&#xff0c;memcmp 三个函数进行详解和模拟实现&#xff1b; 本章重点&#xff1a;3个常见内存函数的使用方法及注意事项并学会模拟实现&#xff1b; 如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力…