百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Java内存模型浅析(java的内存模型和内存结构)

ccwgpt 2024-10-15 08:55 30 浏览 0 评论

Java内存模型即Java Memory Model,简称JMM。JMM定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM隶属于JVM。如果我们想要深入了解Java并发编程,就要先理解好Java内存模型。Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。

两种并发模型

1.共享内存并发模型

在共享内存并发模型中,线程通过对共享对象的写-读来进行隐式通信。另外,在共享内存并发模型中,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。Java使用的就是这种并发模型。在写Java代码时,如果一个线程A想终止另外一个线程B,我们往往会设置一个标志对象在这两个线程之间共享。线程B在运行时不断的检测标志对象是否要停止,而线程A在想结束线程B时,可以将标志对象修改为表示终止的值。不过现在的Java分布式框架AKKA中,使用了Actor并发模型,使消息通信并发模型在Java中也有很好的实现。

2.消息通信并发模型

在消息通信并发模型中,通过发消息显式通信。而同步是隐式的,因为消息的发送必须在消息接收之前。GO语言的并发使用这种模型,称为CSP模型。GO语言中提供了管道的概念,线程A向管道发消息,线程B通过管道接收消息,由此实现线程之间的消息通信。

JMM

在JMM中,有线程本地内存和主内存组成。主内存中的变量,线程是没有办法直接修改的,线程所使用的变量都是从主存读取到自己的本地内存,在本地内存进行修改,之后在刷新回主存。

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

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

通过描述,我们可以知道在没有任何保证措施的情况下,因为不是每次读取数据都会去主存取变量,也不是每次对变量的修改都会马上刷新回主存,一个线程可能看不到另一个线程对共享变量的修改。

指令重排

为了最大的发挥CPU的性能,JVM会对指令进行重排。

1.编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

2.指令级并行的重排序。现在处理器采用了指令级别并行技术来将多条执行重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

3.内存系统的重排序。由于处理器使用缓存和读/写缓存区,这使得加载和存储操作看上去可能是乱序在执行。

内存屏障

为了保证内存可见性,Java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特性类型的处理器重排序。JMM把内存屏障指令分成4类:

happens-before关系

在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。与程序员密切相关的happens-before规则如下:

1.程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。

2.监视锁规则:对于一个锁的解锁,happens-before于随后对这个锁的加锁。

3.voliatile变量规则:对于一个volatile域的写,happens-before于任意后续对这个volatile域的读。

4.传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。

volatile和synchronized

volatile主要有两个作用:

1.保证可见性

2.禁止指令重排

当写一个volatile变量时,JMM会把该线程对应的工作内存中的共享变量值刷新到主内存中,当读一个volatile变量时,JMM会把该线程对应的工作内存置为无效,那么该线程将只能从主内存中重新读取共享变量。volatile变量正是通过这种写-读方式实现对其他线程可见的。

volatile并不保证原子性,也就是说对volatile修饰的变量的复合操作并不具有原子性。比如,一个被volatile修改的变量x,x++并不具有原子性,在多线程的情况下,依然会出现不可预见的结果。

对于可见性的利用,在我们之前的例子中,线程A想通过共享变量的方式来终止线程B,那么这个共享变量就可以用volatile修饰。这样,线程A对变量的修改可以马上被线程B看到。利用禁止指令重排的一个典型例子是单例模式中的double check模式。

在上面的代码中,volatile所起的作用就是禁止指令重排。如果没有volatile修饰,那么可能出现这样的情况:线程A检测实例对象为空,然后进行初始化。类的构造主要分成三步,第一,分配实例所需要的内存;第二,对类进行初始化;第三,返回类的内存地址。这三步是有可能被重排的,先执行第一步,再执行第三步,最后执行第二步。所以当一个new操作返回时,可能这个类还没有被初始化。线程B在线程A new操作返回之后去检测实例对象,此时实例对象不为空,B就直接使用这个对象了,但是这个对象很可能是没有初始化完成的。加入了volatile之后,这种重排序就被禁止了,只要B拿到不为空的对象,那么一定就是已经初始化的了。

synchronized被成为重量级锁,不过现在因为有“锁降级”,它已经不那么重量级了。

1.对于普通同步方法,锁是当前的实例对象。

2.对于静态方法,锁是当前类的Class对象。

3.对于同步方法块,锁是synchronized括号里配置的对象。

参考:《Java并发编程艺术》

相关推荐

RACI矩阵:项目管理中的角色与责任分配利器

作者:赵小燕RACI矩阵RACI矩阵是项目管理中的一种重要工具,旨在明确团队在各个任务中的角色和职责。通过将每个角色划分为负责人、最终责任人、咨询人和知情人四种类型,RACI矩阵确保每个人都清楚自己...

在弱矩阵组织中,如何做好项目管理工作?「慕哲制图」

慕哲出品必属精品系列在弱矩阵组织中,如何做好项目管理工作?【慕哲制图】-------------------------------慕哲制图系列0:一图掌握项目、项目集、项目组合、P2、商业分析和NP...

Scrum模式:每日站会(Daily Scrum)

定义每日站会(DailyScrum)是一个Scrum团队在进行Sprint期间的日常会议。这个会议的主要目的是为了应对Sprint计划中的不断变化,确保团队能够有效应对挑战并达成Sprint目标。为...

大家都在谈论的敏捷开发&Scrum,到底是什么?

敏捷开发作为一种开发模式,近年来深受研发团队欢迎,与瀑布式开发相比,敏捷开发更轻量,灵活性更高,在当下多变环境下,越来越多团队选择敏捷开发。什么是敏捷?敏捷是一种在不确定和变化的环境中,通过创造和响应...

敏捷与Scrum是什么?(scrum敏捷开发是什么)

敏捷是一种思维模式和哲学,它描述了敏捷宣言中的一系列原则。另一方面,Scrum是一个框架,规定了实现这种思维方式的角色,事件,工件和规则/指南。换句话说,敏捷是思维方式,Scrum是规定实施敏捷哲学的...

敏捷项目管理与敏捷:Scrum流程图一览

敏捷开发中的Scrum流程通常可以用一个简单的流程图来表示,以便更清晰地展示Scrum框架的各个阶段和活动。以下是一个常见的Scrum流程图示例:这个流程图涵盖了Scrum框架的主要阶段和活动,其中包...

一张图掌握项目生命周期模型及Scrum框架

Mockito 的最佳实践(mock方法)

记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到...

EffectiveJava-5-枚举和注解(java枚举的作用与好处)

用enum代替int常量1.int枚举:引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式。int枚举模式是类型不安全的,例如下面两组常量:性别和动物种...

Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!

Maven简介Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。Maven是一个跨平台的项目管理工具。主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。仔...

Java单元测试框架PowerMock学习(java单元测试是什么意思)

前言高德的技术大佬在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。”这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入实现...

Spring框架基础知识-第六节内容(Spring高级话题)

Spring高级话题SpringAware基本概念Spring的依赖注入的最大亮点是你所有的Bean对Spring容器的存在是没有意识的。但是在实际的项目中,你的Bean必须要意识到Spring容器...

Java单元测试浅析(JUnit+Mockito)

作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作...

揭秘Java代码背后的质检双侠:JUnit与Mockito!

你有没有发现,现在我们用的手机App、逛的网站,甚至各种智能设备,功能越来越复杂,但用起来却越来越顺畅,很少遇到那种崩溃、卡顿的闹心事儿?这背后可不是程序员一拍脑袋写完代码就完事儿了!他们需要一套严谨...

单元测试框架哪家强?Junit来帮忙!

大家好,在前面的文章中,给大家介绍了以注解和XML的方式分别实现IOC和依赖注入。并且我们定义了一个测试类,通过测试类来获取到了容器中的Bean,具体的测试类定义如下:@Testpublicvoid...

取消回复欢迎 发表评论: