文档章节

java内存模型与线程

qingfeng哥
 qingfeng哥
发布于 2014/06/06 20:54
字数 2935
阅读 265
收藏 4
点赞 0
评论 0

java内存模型与线程

参考

http://baike.baidu.com/view/8657411.htm 

http://developer.51cto.com/art/201309/410971_all.htm

http://www.cnblogs.com/skywang12345/p/3447546.html

计算机的CPU计算能力超强,其计算速度与 内存等存储 和通讯子系统的速度相比快了几个数量级,

数据加载到内存中后,cpu处理器运算处理时,大部分时间花在等待获取去获取磁盘IO、网络通讯、数据库访问返回的数据上。

为什么需要Java Memory Model即java内存模型

目的只有一个:充分利用计算机的各种计算、存储、通信的能力,让ta为人类做更多的事情!  
                    ~CPU使用率 90%以上!

从物理计算机说起:
“做更多的事情”-》让计算机同时做几件事情,即并发执行若干计算任务!
为什么可以并发?
CPU的处理速度是内存的好几倍,而磁盘和网络的速度就更差了
计算机的处理器CPU在计算时,很大一部分时间花在从内存或 磁盘、网络 取 实际数 上!
这一段等待时间可以来处理其他的任务,现在计算机及操作系统都是多任务处理系统!

矛盾: CPU的处理速度比访问内存的速度快3个数量级以上!!!

怎么办:引入 高速缓存   ,在CPU附近,放置速度稍逊CPU的高速缓存,将频繁使用的数据从内存中保存到高速缓存里,让CPU尽可能的告诉运算而减少等待,运算完毕 将结果 回存 到 内存中!

基于高速缓存的架构平衡了CPU和内存间的速度差距

现代多处理器一般抽象架构如下:


上图,多处理器之间 传递交换数据得通过公共的内存区,
这是并发处理时,并发实体间通信的一种模式:基于共享内存
还有的是基于消息(信号量?)的。

新问题:如果公共内存区的某个变量(共享变量
同时被多个处理器 并发操作(RW)时,就会出现数据不一致的问题:
取数据时有可能取到过期的数据,回存数据时,到底以哪个处理器缓存中的数据为准!!!

此外,CPU会对输入的指令进行 乱序执行优化,因此程序代码的出现顺序不一定是其执行顺序!

解决办法:缓存一致性协议(各个硬件平台架构有各自的实现:MSI、MESI、MOSI、Dragon Protocal等)

那jvm既然是一台虚拟的计算机,那也应该能并发处理任务!

于是就有了JMM,java内存模型就是为了屏蔽掉各种硬件和操作系统的内存访问差异,实现java语言在各个平台下都能达到高效、正确、一致的并发处理。
类比多处理器(多核)内存模型,JMM的抽象图如下:

类比:

多核机 JMM 内存
一个处理器 一个线程 os级别的线程(轻量级进程)
高速缓存 工作内存 jvm堆栈,寄存器、高速缓存
缓存一致性协议 多线程同步规则 os级别的调控
内存 主内存 java heap堆,物理内存

java内存模型是指 定义的一套 jvm中变量在 工作内存和主内存 之间的  交互操作 和 操作规则 !
变量:实例字段、静态字段、构成数组对象的成员

JMM将多线程使用的内存分为共享的主内存和线程私有的工作内存,并规定:
所有变量存储于主内存区,变量的生灭都在主内存中
单线程保存需要用到的主内存变量的副本到其工作内存进行操作,不得直接操控读写主内存中的变量
线程只能看到自己工作内存中的变量,线程间数据交换 必须通过 主内存-共享内存的方式!

JMM中的8种变量操作及规则

工作内存和主内存的交互操作一共8种(JSR-133):

操作 作用的变量 效果
lock
主内存 把变量标识为每条线程独占
unlock 主内存 释放某线程的锁
read 主内存
把变量值传输到线程的工作内存
load 工作内存
把传输过来的变量存为本地副本
use 工作内存
将本地变量值传给执行引擎
assign 工作内存
把执行引擎的值写到本地变量
store 工作内存 将本地变量值传输到主内存
write 主内存
将传过来的变量值写入主内存同名变量中

附在这 8个操作上的交互规则(要熟记):
  • read&load   和  store&write这两对操作必须同时出现但两个操作间可以有其他操作,不允许不接受!
  • 一个线程如果有assign操作,则其后必须出现 store和write操作,反之不能出现。改变变量必须回存
  • 新变量只能生灭于主内存,use和store变量前,必须有对应的load或assign该变量的操作
  • 一个变量同一时刻最多允许被一个线程对其lock,同一线程可对这个变量进行多次lock,执行同样次数的unlock才能完全解锁
  • lock变量时,会清空工作内存中变量副本的值,执行引擎使用前需重新load或assign
  • 没有lock的变量不允许unlock
  • 执行unlock前必须回存至主内存,即 store和write

特殊的volatile型变量

变量可以用volatile修饰  如 public static void int race = 0;

那么volatile的语义是啥?

  • jvm中最轻量级的同步保证
  • 保证此变量对所有线程的可见性,变更时线程都能立即感知
  • 禁止jvm对该变量进行指令的重排序(WithinThread As-if-Serial Semantics线程内表现为串行)

解析:被volatile修饰的变量,jvm每次使用use前必须先从主内存中刷来最新值,而且如果有assign操作则必须立刻执行store和write操作,即刻回存主内存中,保证其他线程可以看到当前线程对变量的更改,jvm会插入内存屏蔽指令(memory fence memory barrier)来保障该变量的赋值顺序与程序输入时位置一致,即不会被重排优化。

普通变量的use和assign则没有“每次”和“立即回存”的约束,因此可能混乱!

volatile大多时候比synchronized开销低,以下场景推荐使用volatile,其余请使用synchronized等保障:

  • 变量只被单一线程修改,其他线程读取;
  • 运算结果并不依赖变量的当前值
  • 变量不需要与其他的状态变量共同参与不变约束?

Java内存模型的三个特性

上述操作的讲解其实是jmm围绕并发处理的三个基本保障点:

原子性

什么是原子性:指一个操作,或一系列操作,要么全部执行,要么全没执行!
原子性是要确保你将获得这个变量的初始值或者某个线程对这个变量完全写入之后的值;而不会是两个或更多线程在同一时间对这个变量写入之后产生混乱的结果值(即原子性可以确保,获取到的结果值所对应的所有bit位,全部都是由单个线程写入的
jvm中基本数据类型的读写是原子的
更大范围的原子性保证使用synchronized关键字包裹

内存可见性

可见性是指在一个线程中修改了共享变量本地副本的值,其他线程能立即得知这个修改!assign后主动立即store和write,use前必须read和load
三种实现方式:volatile    synchronized(unlock前必须回存store-write)      final

有序性

在单个线程内所有操作都是有序的:Within-Thread As-if-Serial Semantics

而从一个线程看另一个线程(虽然无法直接感知),则所有操作是无序的:指令重排和工作内存主内存同步延迟

volatile和synchronized(一个变量在同一时刻只允许一个线程lock,这样持对同一变量进行lock的多个同步块只能串行进入)都有保障这点

先行发生原则happen-before

除了volatile,synchronized final这三个关键字对上述三性的保证,jvm还默认提供了规则来保障,否则到处是那三个关键字。

这些默认的规则称为 happen-before原则,是判断数据是否存在竞争,线程是否安全的主要依据!

什么是先行发生:A操作的后果(修改变量值、发送消息、调用了方法)能被B操作感知到!与时间先后几乎无关

先行感知,先行知道,预先发生,不符先行原则的指令很有可能会被jvm执行重排优化。

主要先行感知原则:

  • 程序次序规则program order:同一线程内,按代码、和代码控制流顺序,书写在前的先行于书写在后的
  • 管程锁定规则Monitor lock:一个unlock先行于其时间上之后的对同一个锁的lock操作
  • volatile规则:对volatile变量的写操作先行于后面对他的读操作
  • 线程启动规则:thread对象的start()先行于此对象的其他动作!
  • 线程终止规则:thread的其他动作都先行于对此线程的终止检测。即先行于 Thread.join()|isAlive()
  • 线程中断规则:对线程interrupt()的调用先行于对被中断线程的检测到中断事件的发生,即先行于Thread.interrupted()
  • 对象终结规则:一个对象的初始化完成(构造函数完毕)先行于其finalize()方法
  • 传递性:A先行于B,B先行于C,则 A先行于C

时间上的先后顺序 与 先生发生原则之间没有太大关系,衡量并发安全问题的时候以 先行发生原则为准!

Java语言的线程实现

java线程

比进程更轻量的调度执行单元

java被native声明的方法都是平台相关的,不过也是最高效的

线程实现方式,看是谁来掌控线程的调度切换:
操作系统内核级别的多线程调度支持-轻量级进程(内核线程)1:1,一个进程:一个线程
用户态自行掌控线程,1:N,一个进程:多个线程
用户线程+轻量级进程,可以M:N,多对多

sun jdk的window和Linux版采用平台相关的os级一对一的轻进程模型。

并发的表现方式:单进程多线程jvm  多进程单线程php  多进程多线程powerpc

线程调度

调度:系统为线程分配处理器使用权的过程!

协同式:cooperative

线程本身控制执行时间,完成任务后,要主动通知系统切换到另一个线程上去,不稳定!

抢占式:Preemptive

os来分配线程的执行时间和切换,执行时间相对可控。Java线程如此调度,线程的优先级得由os的线程优先级保证。

线程状态转换

java定义了线程的5种状态:new runable waiting ,timed waiting , blocked,terminated

http://my.oschina.net/mingdongcheng/blog/139263









© 著作权归作者所有

共有 人打赏支持
qingfeng哥

qingfeng哥

粉丝 44
博文 122
码字总数 63706
作品 0
湛江
技术主管
11、Java并发性和多线程-Java内存模型

以下内容转自http://ifeve.com/java-memory-model-6/: Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的。Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内...

easonjim ⋅ 2017/06/15 ⋅ 0

【JVM】 java内存区域与内存溢出异常

前言 此系列博客是读《深入理解java虚拟机》所做的笔记整理。 No1. JVM内存管理这堵墙? 对C和C++的开发人员来说,在内存管理领域,他们既拥有每一个对象的“所有权”,也担负着每一个对象生...

binggetong ⋅ 05/07 ⋅ 0

JAVA虚拟机 JVM 详细分析 原理和优化(个人经验+网络搜集整理学习)

JVM是java实现跨平台的主要依赖就不具体解释它是什么了 ,简单说就是把java的代码转化为操作系统能识别的命令去执行,下面直接讲一下它的组成 1.ClassLoader(类加载器) 加载Class 文件到内...

小海bug ⋅ 06/14 ⋅ 0

Java 面试知识点解析(三)——JVM篇

前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大部...

我没有三颗心脏 ⋅ 05/16 ⋅ 0

Java 编程之美:并发编程基础晋级篇

本文来自作者 加多 在 GitChat 上分享 「Java 并发编程之美:并发编程基础晋级篇」 编辑 | Mc Jin 借用 Java 并发编程实践中的话,编写正确的程序并不容易,而编写正常的并发程序就更难了! ...

gitchat ⋅ 04/18 ⋅ 0

聊聊JAVA虚拟机中的垃圾收集器

前言 JAVA虚拟机的垃圾收集器是虚拟机内存的清道夫,它的存在让JAVA开发人员能将更多精力投入到业务研发上。了解垃圾收集器,并利用好这个工具,能更好的保障服务稳定性。这篇文章通过分析J...

lilugoodjob ⋅ 05/13 ⋅ 0

Java虚拟机--内存模型与线程

Java虚拟机--内存模型与线程 高速缓存:处理器要与内存交互,如读取、存储运算结果,而计算机的存储设备和处理器的运算速度差异巨大,所以加入一层读写速度和处理器接近的高速缓存来作为内存...

sunhaiyu ⋅ 昨天 ⋅ 0

培训云计算学校,虚拟机基本结构讲解

我们要对JVM虚拟机的结构有一个感性的认知。毕竟我们不是编程人员,认知程度达不到那么深入。一个运行时的Java虚拟机实例的天职是:负责运行一个java程序。当启动一个Java程序时,一个虚拟机...

长沙千锋 ⋅ 05/17 ⋅ 0

Java堆和栈的区别,JVM的堆和栈的介绍

一、Java的堆内存和栈内存 Java把内存划分成两种:一种是堆内存,一种是栈内存。 堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个JVM只有一个堆内存,线程是可以共享数据的...

光明辉煌 ⋅ 05/21 ⋅ 0

Java堆和栈的区别和介绍以及JVM的堆和栈

Java堆和栈的区别和介绍以及JVM的堆和栈 一、Java的堆内存和栈内存 Java把内存划分成两种:一种是堆内存,一种是栈内存。 堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个...

代金券优惠 ⋅ 05/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 3分钟前 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 18分钟前 ⋅ 0

eclipse酷炫大法之设置主题、皮肤

eclipse酷炫大法 目前两款不错的eclipse 1.系统设置 Window->Preferences->General->Appearance 2.Eclipse Marketplace下载【推荐】 Help->Eclipse Marketplace->搜索‘theme’进行安装 比如......

anlve ⋅ 27分钟前 ⋅ 0

vim编辑模式、vim命令模式、vim实践

vim编辑模式 编辑模式用来输入或修改文本内容,编辑模式除了Esc外其他键几乎都是输入 如何进入编辑模式 一般模式输入以下按键,均可进入编辑模式,左下角提示 insert(中文为插入) 字样 i ...

蛋黄Yolks ⋅ 31分钟前 ⋅ 0

大数据入门基础:SSH介绍

什么是ssh 简单说,SSH是一种网络协议,用于计算机之间的加密登录。 如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码...

董黎明 ⋅ 50分钟前 ⋅ 0

web3j教程

web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 汇智网最新发布的web3j教程,详细讲解...

汇智网教程 ⋅ 57分钟前 ⋅ 0

谷歌:安全问题机制并不如你想象中安全

腾讯科技讯 5月25日,如今的你或许已经对许多网站所使用的“安全问题机制”习以为常了,但你真的认为包括“你第一个宠物的名字是什么?”这些问题能够保障你的帐户安全吗? 根据谷歌(微博)安...

问题终结者 ⋅ 57分钟前 ⋅ 0

聊聊spring cloud gateway的RedisRateLimiter

序 本文主要研究下spring cloud gateway的RedisRateLimiter GatewayRedisAutoConfiguration spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/con......

go4it ⋅ 今天 ⋅ 0

169. Majority Element - LeetCode

Question 169. Majority Element Solution 思路:构造一个map存储每个数字出现的次数,然后遍历map返回出现次数大于数组一半的数字. 还有一种思路是:对这个数组排序,次数超过n/2的元素必然在中...

yysue ⋅ 今天 ⋅ 0

NFS

14.1 NFS介绍 NFS是Network File System的缩写 NFS最早由Sun公司开发,分2,3,4三个版本,2和3由Sun起草开发,4.0开始Netapp公司参与并主导开发,最新为4.1版本 NFS数据传输基于RPC协议,RPC...

派派菠菜 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部