当前位置: 首页 > news >正文

【并发编程】深入理解Java内存模型及相关面试题

文章目录

    • 优秀引用
    • 1、引入
    • 2、概述
    • 3、JMM内存模型的实现
      • 3.1、简介
      • 3.2、原子性
      • 3.3、可见性
      • 3.4、有序性
    • 4、相关面试题
      • 4.1、你知道什么是Java内存模型JMM吗?
      • 4.2、JMM和volatile他们两个之间的关系是什么?
      • 4.3、JMM有哪些特性/能说说JMM的三大特性吗?
      • 4.4、为什么要有JMM,它为什么会出现,作用和功能是什么
      • 4.5、有了解过happens-before原则吗?

优秀引用

全面理解Java的内存模型(JMM)

终于有人把Java内存模型(JMM)说清楚了

1、引入

对于Java虚拟机的内存模型相信大家都不陌生了,对于每一个线程来说,栈是私有的,而堆是共享的,也就是说在栈中的变量(局部变量、方法定义参数、异常处理器参数)不会在线程之间共享,也就不会有内存可见性的问题,也不受内存模型的影响。

img

既然堆中的数据是线程共享的,那么一定会存在一个并发问题,但由于每个线程都有自己的本地缓存,导致线程之间读取的数据可能不是最新的,从而出现数据不一致的问题。JMM定义了一种内存模型,用于描述Java程序中多线程之间如何访问共享内存中的变量,从而解决了这个问题。

这里扯一嘴,在Java中使用的是共享内存并发模型,用于解决Java中线程间如何通信和数据同步的问题。

2、概述

JMM(Java Memory Model)是Java内存模型的缩写,是一种抽象的概念,定义了Java虚拟机如何在计算机内存中存储和访问Java对象的方法。JMM规范主要用于解决多线程访问共享内存时的可见性、有序性和原子性问题。下面是JMM的抽象示意图:
JMM抽象示意图

从上图中可以看出:

  • 所有的共享变量都存在主内存中;

  • 每个线程都保存了一份该线程使用到的共享变量的副本

  • 如果线程A与线程B之间要通信的话,必须经历下面2个步骤:

    1. 线程A将本地内存A中更新过的共享变量刷新到主内存中去。

    2. 线程B到主内存中读取线程A之前已经更新过的共享变量。

因此,线程A无法直接访问线程B的工作内存,那是因为工作内存是线程独有的,线程间通信必须借助主内存,这也是JMM中的规定。当主内存中的共享变量被某个线程更新时,JMM会通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证。因此通过JMM规范,有效的解决了以下问题:

  • 可见性问题:JMM保证对于一个线程对变量的修改,其他线程能够立即看到这个修改,从而避免了线程之间读取数据不一致的问题;
  • 有序性问题:JMM保证程序的执行顺序是有序的,即按照代码的编写顺序执行,从而避免了出现代码执行顺序混乱的问题;
  • 原子性问题:JMM保证对单个变量的读取和写入操作是原子性的,即不会出现数据竞争问题。

3、JMM内存模型的实现

3.1、简介

在Java中存在着许多并发相关的关键字,如valatile、synchronized、final等,而这些被大众所熟知的关键字便是Java内存模型封装了底层的实现后提供给开发人员进行使用。因此Java内存模型除了定义了一套规范,还提供了一系列的封装了底层实现的关键字供开发者使用。

3.2、原子性

原子性指的是指一个操作是不可中断的,即多线程环境下,操作不能被其他线程干扰。在Java中,最常用的便是使用关键字synchronized进行原子性的保证。

3.3、可见性

指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道该变更。对于Java中的普通共享变量是不保证可见性的,因为数据修改被写入内存的时机是不确定的,多线程并发下很可能会出现脏读的并发问题,因此便有着上面提及到的线程私有的工作内存

每个线程的工作内存都保存了一份该线程使用到共享变量的副本,即主内存中的拷贝副本,在操作数据时只能在本地的副本中进行操作,不能直接对主内存中的数据进行操作。同时不同线程间也是不能直接进行通讯的,必须借助主内存来实现。

而在Java中提供了volatile、synchronized、final关键字来保证多线程操作时变量的可见性,其中volatile关键字提供了这么一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存中,被其他修饰的变量在每次使用之前都从主内存中刷新获取,从而保证可见性。

3.4、有序性

指程序是有序的按照一定的顺序运行,这一特性主要是针对于操作系统中对程序指令进行重排序造成的并发乱序问题。为了性能和便捷,在JMM中指明,再不改变程序执行结果的前提下,允许编译器和处理器对程序优化进行重排序。

在Java中,可以使用synchronizedvolatile来保证多线程之间操作的有序性。实现方式有所区别:

  • volatile关键字会禁止指令重排;
  • synchronized关键字保证同一时刻只允许一条线程操作。

值得注意的是,按照一定的顺序并不一定是代码中的顺序,典型的例子便是线程的创建和执行,下面的示例代码便是先执行第四行后执行第二行:

Thread t1 = new Thread(() -> {System.out.println("hello thread");
}, "t1");
t1.start();

4、相关面试题

4.1、你知道什么是Java内存模型JMM吗?

JMM(Java内存模型)是Java虚拟机规范中定义的一种内存模型,用于描述Java程序中多线程之间如何访问共享内存中的变量。JMM规定了Java程序中的所有变量都存储在主内存中,每个线程都有自己的本地缓存,线程对变量的读取和写入操作都必须在本地缓存和主内存之间进行同步,以保证数据的可见性、原子性和有序性。

JMM定义了一些规则和约束,如volatile关键字、synchronized关键字等,用于保证程序的正确性和稳定性。使用volatile关键字可以保证变量的可见性,即每个线程都能看到其他线程对该变量的最新修改。使用synchronized关键字可以保证代码块的原子性,即同一时刻只有一个线程能够执行该代码块。

4.2、JMM和volatile他们两个之间的关系是什么?

volatile是对JMM底层实现封装的关键字之一,是JMM的一种实现方式,用于修饰变量,表示该变量是可见性的,即任何对该变量的修改都会立即被其他线程所看到。volatile关键字是JMM的一种实现方式,它可以保证变量的可见性。

在Java中,使用volatile关键字修饰的变量会被存储在主内存中,每个线程访问该变量时都会从主内存中读取最新的值,而不是从线程的本地缓存中读取。因此,使用volatile关键字可以保证变量的可见性,避免出现数据不一致的问题。

同时,volatile关键字还具有禁止指令重排序的作用,即保证代码的有序性。由于JMM规定了线程之间的操作可能会存在重排序的情况,因此使用volatile关键字可以禁止指令重排序,保证程序的正确性和稳定性。

4.3、JMM有哪些特性/能说说JMM的三大特性吗?

JMM(Java内存模型)有三个特性,也被称为JMM的三大特性,分别是原子性、可见性和有序性。

  1. 原子性:原子性是指对于单个变量的读取和写入操作是原子性的,即不会出现数据竞争问题。在JMM中,原子性是通过synchronized关键字和volatile关键字来保证的。
  2. 可见性:可见性是指当一个线程对一个变量进行修改时,其他线程能够立即看到这个修改,从而避免了线程之间读取数据不一致的问题。在JMM中,可见性是通过volatile关键字来保证的。
  3. 有序性:有序性是指程序的执行顺序是有序的,即按照代码的编写顺序执行,从而避免了出现代码执行顺序混乱的问题。在JMM中,有序性是通过happens-before规则来保证的。

happens-before规则定义了程序执行中各个操作之间的偏序关系,主要述说的是,某一段符合该规则的代码,前面代码得到的结果肯定对后方代码是可见的。

4.4、为什么要有JMM,它为什么会出现,作用和功能是什么

在Java多线程编程中,由于多个线程之间共享数据的情况很常见,因此需要一种机制来保证多线程之间的数据访问是正确的。在JVM中,由于每个线程都有自己的本地缓存,因此线程之间读取的数据可能不是最新的,从而出现数据不一致的问题。为了解决这个问题,Java引入了JMM。

JMM的作用就是规定了一些规则,保证多线程访问共享变量的正确性。JMM主要有以下几个功能:

  1. 确定共享变量的可见性:JMM规定,如果一个线程修改了共享变量的值,其他线程必须能够立即看到这个变化。
  2. 确定操作的有序性:JMM规定,如果一个线程对共享变量进行了多次修改,其他线程必须按照这些修改的顺序来看到这些变化。
  3. 确定操作的原子性:JMM规定,如果一个线程在修改共享变量的过程中,其他线程必须等待这个修改完成后再进行操作。

4.5、有了解过happens-before原则吗?

happens-before原则是Java内存模型中一个重要的概念,用于指定多线程程序中操作之间的顺序关系。

happens-before原则规定,如果操作A happens-before操作B,那么操作A的效果在操作B之前对其他线程是可见的。也就是说,如果在一个线程中操作A happens-before操作B,那么在另一个线程中看到操作A的效果一定是在看到操作B的效果之前。

相关文章:

【并发编程】深入理解Java内存模型及相关面试题

文章目录优秀引用1、引入2、概述3、JMM内存模型的实现3.1、简介3.2、原子性3.3、可见性3.4、有序性4、相关面试题4.1、你知道什么是Java内存模型JMM吗?4.2、JMM和volatile他们两个之间的关系是什么?4.3、JMM有哪些特性/能说说JMM的三大特性吗&#xff1f…...

C++编程语言STL之queue介绍

本文主要介绍C编程语言的STL(Standard Template Library)中queue(队列)的相关知识,同时通过示例代码介绍queue的常见用法。1 概述适配器(adaptor)是STL中的一个通用概念。容器、迭代器和函数都有…...

ACO优化蚁群算法

%% 蚁群算法(ant colony optimization,ACO) %清空变量 clear close all clc [ graph ] createGraph(); figure subplot(1,3,1) drawGraph( graph); %% 初始化参数 maxIter 100; antNo 50; tau0 10 * 1 / ( graph.n * mean( graph.edges(:) …...

SwiftUI 常用组件和属性(SwiftUI初学笔记)

本文为初学SwiftUI笔记。记录SwiftUI常用的组件和属性。 组件 共有属性(View的属性) Image("toRight").resizable().background(.red) // 背景色.shadow(color: .black, radius: 2, x: 9, y: 15) //阴影.frame(width: 30, height: 30) // 宽高 可以只设置宽或者高.…...

Centos 中设置代理的两种方法

Centos 中设置代理的两种方法 在使用局域网时,有时在局域网内只有一台电脑可以进行上网,其他电脑只能通过配置代理的方式来上网,在Windows系统中设置代理上网相对简单,如果只需上网的话,只需在浏览器中找到网络连接&am…...

高速PCB设计指南系列(一)

第一篇 PCB布线 在PCB设计中,布线是完成产品设计的重要步骤,可以说前面的准备工作都是为它而做的, 在整个PCB中,以布线的设计过程限定最高,技巧最细、工作量最大。PCB布线有单面布线、 双面布线及多层布线。布线的方…...

云端IDE:TitanIDE v2.6.0 正式发布

原文作者:行云创新技术总监 邓冰寒 引言 云原生集成开发环境 TitanIDE v2.6.0 正式发布了,一起来看看都有那些全新的体验吧! TitanIDE 是一款云IDE, 也称 CloudIDE,作为数字化时代研发体系不可或缺的一环,和企业建设…...

【Python】tqdm 模块

import mathfrom tqdm import tqdm, trange# 计算阶乘 results_1 []for i in range(6666):results_1.append(math.factorial(i))这是一个循环计算阶乘的程序,我们不知道程序运行的具体情况,如果能加上一个程序运行过程的进度条,那可就太有趣…...

论文阅读:Adversarial Cross-Modal Retrieval对抗式跨模式检索

Adversarial Cross-Modal Retrieval 对抗式跨模式检索 跨模态检索研究的核心是学习一个共同的子空间,不同模态的数据可以直接相互比较。本文提出了一种新的对抗性跨模态检索(ACMR)方法,它在对抗性学习的基础上寻求有效的共同子空间…...

计算机网络复习

什么是DHCP和DNS DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的…...

unity动画--动画绑定,转换,用脚本触发

文章目录如何制作和添加动画大概过程示例图将多组图片转化为动画放在对象身上实现动画之间的切换使用脚本触发Parameters(Trigger)如何制作和添加动画 大概过程示例图 将多组图片转化为动画放在对象身上 首先,我们要为我们要对象添加animator 然后我们要设置对应的…...

车载汽车充气泵PCBA方案

汽车为什么会需要充气泵呢?其实是由于乘用车中没有供气源,所以就必需充气泵来给避震器供气。充气泵是为了保障汽车车胎对汽车的行驶安全所配备的,防止遇上紧急问题时没有解决方案,同时也可以检测轮胎胎压。现阶段的充气泵方案&…...

Android 连接 MySQL 数据库教程

在 Android 应用程序中连接 MySQL 数据库可以帮助开发人员实现更丰富的数据管理功能。本教程将介绍如何在 Android 应用程序中使用低版本的 MySQL Connector/J 驱动程序来连接 MySQL 数据库。 步骤一:下载 MySQL Connector/J 驱动程序 首先,我们需要下…...

tmall.item.update.schema.get( 天猫编辑商品规则获取 )

¥开放平台免费API必须用户授权 Schema方式编辑天猫商品时,编辑商品规则获取 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 点击获取key和secret 请求示例 TaobaoClient client new DefaultTaobao…...

Leetcode 2379. 得到 K 个黑块的最少涂色次数

目录 一、题目内容和对应链接 1.题目对应链接 2.题目内容 二、我的想法 三、其他人的题解 一、题目内容和对应链接 1.题目对应链接 Leetcode 2379. 得到 K 个黑块的最少涂色次数 2.题目内容 给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[i] 要…...

[深入理解SSD系列 闪存实战2.1.3] 固态硬盘闪存的物理学原理_NAND Flash 的读、写、擦工作原理

2.1.3.1 Flash 的物理学原理与发明历程 经典物理学认为 物体越过势垒,有一阈值能量;粒子能量小于此能量则不能越过,大于此能 量则可以越过。例如骑自行车过小坡,先用力骑,如果坡很低,不蹬自行车也能 靠惯性过去。如果坡很高,不蹬自行车,车到一半就停住,然后退回去。 …...

总结:Linux内核相关

一、介绍看eBPF和Cilium相关内容时,碰到Cilium是运行在第 3/4 层,不明白怎么做到的,思考原理的时候就想到了内容,本文记录下内核相关知识。https://www.oschina.net/p/cilium?hmsraladdin1e1二、Linux内核主要由哪几个部分组成Li…...

flutter工程创建过程中遇到一些问题。

安装环境版本:JDK7.-JDK 8 Andriod SDK 10 flutter 版本 3.0 1.当创建完后flutter工程后会遇到 run gradle task assemlble Debug 的问题,需要设置远程仓库,共需要修改三个地方build.gradle两处以及flutter 下面的D:\FVM\versions\3.0.0\pac…...

记录实现操作系统互斥锁的一次思考

今天实现操作系统互斥锁的时候遇到一个有趣的问题。 场景 有两个进程分别名为 taskA,taskB,采取时间片轮转的方式交替运行——也即维护了一个 ready_queue,根据时钟中断来 FIFO 地调度任务。它们的任务是无限循环调用 sys_print() 来打印自…...

计算机SCI期刊的分值是什么意思? - 易智编译EaseEditing

影响因子(Impact Factor,IF)是美国ISI(科学信息研究所)的JCR(期刊引证报告)中的一项数据。 即某期刊前两年发表的论文在统计当年的被引用总次数除以该期刊在前两年内发表的论文总数。这是一个国际上通行的期刊评价指标。 例如,某期刊2005年影…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...