作者:killianxu

来源:https://www.cnblogs.com/killianxu/p/11665903.html

java内存模型知识导图

一 并发问题及含义

并发编程存在原子性、可见性、有序性问题。

  1. 原子性即一系列操作要么都执行,要么都不执行。
  2. 可见性,一个线程对共享变量的修改,另一个线程可能不会马上看到。由于多核CPU,每个CPU核都有高速缓存,会缓存共享变量,某个线程对共享变量的修改会改变高速缓存中的值,但却不会马上写入内存。另一个线程读到的是另一个核缓存的共享变量的值,出现缓存不一致问题。
  3. 有序性,即程序执行的顺序按照代码的先后顺序执行。编译器和处理器会对指令进行重排,以优化指令执行性能,重排不会改变单线程执行结果,但在多线程中可能会引起各种各样的问题。

二 内存模型

为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。内存模型解决并发问题

主要采用两种方式:限制处理器优化和使用内存屏障。

顺序一致性内存模型是一种理论参考模型,提供了极强的内存可见性保证,具有两大特性:

  1. 一个线程的所有操作按照程序的顺序执行,而不能重排序。
  2. 所有线程只能看到单一的执行顺序。每个操作都必须原子执行且立刻对其它线程可见。

顺序一致性内存模型禁止很多处理器和编译器重排,影响执行性能,处理器内存模型和JMM对顺序一致性内存模型进行放松,执行性能:处理器内存模型>JMM>顺序一致性内存模型,易编程性:处理器内存模型

三 java内存模型

Java内存模型(Java Memory Model ,JMM)是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。主内存和工作内存可类比成计算机内存模型中的主存和缓存的概念。

3.1 java内存模型解决并发问题方法

原子性,在java中,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。在32位平台下,对64位数据的赋值是需要通过两个操作来完成,不能保证其原子性。要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock保证任一时刻只有一个线程执行该代码块,从而保证了原子性。

可见性,Java提供了volatile关键字来保证可见性,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

JMM通过happens-before关系向程序员提供跨线程的内存可见性保证:

  1. 程序次序规则:一段代码在单线程中执行的结果是有序的。注意是执行结果,因为虚拟机、处理器会对指令进行重排序(重排序后面会详细介绍)。虽然重排序了,但是并不会影响程序的执行结果,所以程序最终执行的结果与顺序执行的结果是一致的。故而这个规则只对单线程有效,在多线程环境下无法保证正确性。
  2. 锁定规则:这个规则比较好理解,无论是在单线程环境还是多线程环境,一个锁处于被锁定状态,那么必须先执行unlock操作后面才能进行lock操作。
  3. volatile变量规则:这是一条比较重要的规则,它标志着volatile保证了线程可见性。通俗点讲就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作一定是happens-before读操作的。
  4. 传递规则:提现了happens-before原则具有传递性,即A happens-before B , B happens-before C,那么A happens-before C
  5. 线程启动规则:假定线程A在执行过程中,通过执行ThreadB.start()来启动线程B,那么线程A对共享变量的修改在接下来线程B开始执行后确保对线程B可见。
  6. 线程终结规则:假定线程A在执行的过程中,通过制定ThreadB.join()等待线程B终止,那么线程B在终止之前对共享变量的修改在线程A等待返回后可见。

有序性,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别:volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。

3.2 java并发原语

Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。

3.2.1 volatile

内存语义:

当写一个volatile变量时,JMM会把该线程对应的本地内存中的所有共享变量刷新到主内存。

当读一个volatile变量,JMM会把该线程对应的本地内存置为无效,线程接下来从主内存中读取共享变量。

实现:

编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

在每个volatile写操作前面插入一个StoreStore屏障。StoreStore屏障禁止上面的普通写和volatile写重排序,保障上面的普通写在volatile写之前刷新到主内存。

在每个volatile写操作后面插入一个StoreLoad屏障。避免volatile写与后面可能有的volatile读/写重排序。

在每个volatile读操作的后面插入一个LoadLoad屏障。禁止下面的普通读操作和上面的volatile读操作重排序

在每个volatile读操作的后面插入一个LoadStore屏障。禁止下面的普通写操作和上面的volatile读操作重排序

3.2.2 synchronized

内存语义:

当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中.

当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量.

实现:

java对象头组成:

  1. Mark Word
  2. 指向类的指针
  3. 数组长度(只有数组对象才有)

Mark Word用于加锁操作,结构如下:

图3.1 java对象头Mark Word

synchronized用的锁是存在Java对象头里,任何java对象都存在一个锁,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。代码块同步是使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处。

监视器锁(Monitor)本质依赖操作系统的Mutex Lock(互斥锁)来实现,如果互斥量已经上锁,调用线程会阻塞,阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。在jdk1.6中加入对锁的优化措施,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。锁可以升级但不能降级。

偏向锁:

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。

轻量级锁:

轻量级锁是为了在线程近乎交替执行同步块时提高性能。多个线程竞争锁,若当前只有一个等待线程,则可通过自旋稍微等待一下,可能另一个线程很快就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁。

重量级锁:

重量级锁是通过对象内部的一个叫做监视器锁(monitor)来实现的,监视器锁本质又是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的。而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高。

其它锁优化措施:锁消除、锁粗化、自旋锁(忙循环,适用持有锁的线程很快释放锁)、自适应的自旋锁(自旋次数不固定,前一次在同一个锁上的自旋时间及锁的拥有者的状态决定)。

3.2.3 final

写final域禁止把final域的写重排序到构造函数之外。对于引用类型:在构造函数内对final域引用对象的成员域的写入,与在构造函数外将这个被构造对象的引用赋值给引用变量,这两个操作不能重排序。防止对象构造完成,未被初始化的final域被访问(要达到此目的,还需确保被构造对象不能在构造函数中“逸出”)

读final域禁止初次读一个对象的引用和随后初次读这个对象包含的final域之间的重排序。确保在读一个对象的final域前,一定会先读包含这个final域对象的引用,如果引用不为空,引用对象的final域已经被初始化过。

实现:

JMM禁止编译器把final域的写重排序到构造函数之外。

编译器在final域的写之后,构造函数return之前,插入StoreStore屏障,禁止处理器把final域的写重排序到构造函数之外。

编译器会在读final域前面插入StoreStore屏障。

内存位置访问无效 midas.dll_java并发之内存模型相关推荐

  1. 内存位置访问无效 midas.dll_内存虚拟化介绍

    本文以Intel开源的ACRN项目(Homepage - Project ACRN™)介绍的内存虚拟化技术为基础,介绍一下在虚拟系统中内存管理的一些基本知识.另外不同虚拟化公司提供的技术大多基于开源项 ...

  2. 内存位置访问无效_万字长文——java内存模型之volatile深入解读

    在阅读本文前,请思考以下的面试题? volatile是什么? volatile的特性 volatile是如何保证可见性的? volatile是如何保证有序性的? volatile可以保证原子性吗? 使 ...

  3. System.DllNotFoundException: 无法加载 DLL“GdltaxIA.dll”: 内存位置访问无效。 (异常来自 HRESULT:0x800703E6)。 WIN7系统

    System.DllNotFoundException: 无法加载 DLL"GdltaxIA.dll": 内存位置访问无效. (异常来自 HRESULT:0x800703E6). ...

  4. PHP“内存位置访问无效”

    新配置的PHP环境,或者刚做过配置改动,比如新加载的DLL扩展,访问页面,可能会出现"内存位置访问无效."的错误.主要原因是DLL扩展加载失败. 我们就需要找到是哪一个DLL文件加 ...

  5. 如何访问固定的内存位置?

    访问固定的内存位置(Accessing fixed memory locations) [提问] 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点.在某工程中,要求设置一绝对地址为0x67a9 ...

  6. system v和posix的共享内存对比 共享内存位置

    参考 http://www.startos.com/linux/tips/2011012822078.html 1)Linux和所有的UNIX操作系统都允许通过共享内存在应用程序之间共享存储空间. 2 ...

  7. php 静态类内存,php面向对象中static静态属性与方法的内存位置分析

    本文实例分析了php面向对象中static静态属性与方法的内存位置.分享给大家供大家参考.具体如下: static静态属性的内存位置-->类,而不是对象.下面做测试来证明一下 header(&q ...

  8. 转载浅谈MFC内存泄露检测及内存越界访问保护机制

    2019独角兽企业重金招聘Python工程师标准>>> 本文所有代码均在VC2008下编译.调试.如果您使用的编译器不同,结果可能会有差别,但本文讲述的原理对于大部分编译器应该是相似 ...

  9. c memcpy 同内存_C / C ++ memcpy()–跨内存位置复制

    c memcpy 同内存 The memcpy() function in C/C++ is used to copy data from one memory location to another ...

最新文章

  1. Python3学习笔记-数据类型和变量
  2. Oracle里default什么意思,ORACLE中默认值default的使用方法.doc
  3. #时间预测算法_【时间序列】时序预测竞赛之异常检测算法综述
  4. 动手学CV-目标检测入门教程4:模型结构
  5. 提取网页的table时,遇到table中的两行(tr)中间有空行(或无空行)的正则表达式我的处理
  6. P问题、NP问题、NPC问题、NP hard问题
  7. 论文写作中插入公式间距变大怎么办?
  8. 计算机家庭网络共享,Windows7创建家庭组实现多台电脑之间共享资源
  9. Libre 6008 「网络流 24 题」餐巾计划 (网络流,最小费用最大流)
  10. 20200227——Spring 框架的设计理念与设计模式分析
  11. 中国浓缩咖啡机行业市场供需与战略研究报告
  12. HCIA Storage部分题库
  13. 编译原理(四) 消除回溯提取左因子法
  14. 网站服务器拥挤如何进去,教你一招:有效解决网络拥挤的办法!
  15. Mac电脑Safari 浏览器中Cookie 和网站数据如何管理
  16. Win10+Python3+OpenCV+CUDA——在win中配置OpenCV4.5并与Python环境绑定
  17. 魔兽世界linux客户端,使用Wine在Linux下玩魔兽世界
  18. 谈谈算法(数据结构学习笔记)
  19. Numpy和spicy函数复习1
  20. 知识付费领域市场格局与投资观察

热门文章

  1. PyCharm 安装插件
  2. C++ list,STL list
  3. Jsoup消除不受信任的HTML(用于防止XSS的攻击)
  4. 计算机二维动画的核心技术是什么意思,数字化技术在二维动画设计中的应用研究...
  5. 系统仿真平台SkyEye可替代国外Matlab/Sumlink等同类软件
  6. object detection之Win10配置
  7. 计算机网络技术三级做题技巧,三级网络技术——我的经历,我的技巧
  8. 吴恩达机器学习笔记(二) —— Logistic回归
  9. python 字典练习 记录学生是否交作业的小程序
  10. Android 中.aar文件生成方法与用法