java内存模型与线程(转) good
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
,
http://my.oschina.net/jingxing05/blog/275334
java内存模型与线程(转) good的更多相关文章
- Java并发程序设计(三) Java内存模型和线程安全
Java内存模型和线程安全 一 .原子性 原子性是指一个操作是不可中断的.即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰. 思考:i++是原子操作吗? 二.有序性 Java代 ...
- 深入理解java虚拟机-第12章Java内存模型与线程
第12章 Java内存模型与线程 Java内存模型 主内存与工作内存: java内存模型规定了所有的变量都在主内存中,每条线程还有自己的工作内存. 工作内存中保存了该线程使用的主内存副本拷贝,线程对 ...
- jvm(12)-java内存模型与线程
[0]README 0.1)本文部分文字描述转自“深入理解jvm”,旨在学习“java内存模型与线程” 的基础知识: [1]概述 1)并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计 ...
- (Java多线程系列七)Java内存模型和线程的三大特性
Java内存模型和线程的三大特性 多线程有三大特性:原子性.可见性.有序性 1.Java内存模型 Java内存模型(Java Memory Model ,JMM),决定一个线程对共享变量的写入时,能对 ...
- 深入理解Java虚拟机(第三版)-13.Java内存模型与线程
13.Java内存模型与线程 1.Java内存模型 Java 内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到主内存和从内存中取出变量值的底层细节 该变量指的是 实例字 ...
- 一夜搞懂 | Java 内存模型与线程
前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习内存模型与线程? 并发处理的广泛应用是 Amdah1 定律代替摩尔定律成为计 ...
- Java内存模型与线程(一)
Java内存模型与线程 TPS:衡量一个服务性能的标准,每秒事务处理的总数,表示一秒内服务端平均能够响应的总数,TPS又和并发能力密切相关. 在聊JMM(Java内存模型)之前,先说一下Java为什么 ...
- 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
<深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...
- java内存模型和线程
概述 多任务的处理在现在的计算机中可以说是"标配"了,在许多的情况下,让计算机同时做几件事情,不仅是因为计算机的运算能力的强大,还有一个重要的原因是:cpu的运算速度和计算机的存储 ...
随机推荐
- Week2(9月16日):动手做个简单的例子
Part I:提问 =========================== 1.什么是ASP.NET MVC? 2.MVC的英文? 3.什么是模型? 4.什么是控制器? 5.什么是视图? 6.ASP ...
- 转:CI引入外部js与css
其实不管是在用CI还是ZF都有同样一个问题,就是路径的问题.前期,我在用ZF做CMS时,我在.htaccess文件中设置了如遇到js,css,img等资源文件都不重定向.但今天在用CI时,却忘记了,搞 ...
- JSpider是一个用Java实现的WebSpider
JSpider是一个用Java实现的WebSpider,JSpider的执行格式如下: jspider [URL] [ConfigName] URL一定要加上协议名称,如:http://,否则会报错. ...
- vim-ctags-taglist-netrw
vim配置 在~/.vimrc文件里配置例如以下内容.或者在/etc/vim/vimrc中进行全局配置,经常使用配置例如以下: syntax on set tabstop=4 set nu set s ...
- linux 单网卡绑定两个ip
一.ubuntu系统: #vi /etc/network/interfaces OR $ sudo vi /etc/network/interfaces Modify as follows: au ...
- 工作随记 warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
错误信息:F:\BUILD\IDS7020\trunk\manage_src\dev\java_src\tds7030-web\Ant\build.xml:344: warning: 'include ...
- Mac删除废纸篓中的单一文件和文件夹
http://www.macappbox.com/tips/159/ 通过Automator创建教程: 1.打开Automator并选择新建 2.选择服务类型 3.搜索Run Shell Script ...
- FireMonkey下的异形窗体拖动(句柄转换)
DelphiXE2 Firemoney FMX 的窗体不只是为windows的, 所以很多功能都没有了. 最常见的就是拖拽了 先看 VCL时代 一个经典拖动代码 ReleaseCapture(); S ...
- 基于visual Studio2013解决算法导论之002归并排序
题目 归并排序 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...
- 私有析构函数 Android 代码分析
有人说声明 Private Destructor, 这对象只能在 stack 上创建,不能在Heap上创建, 其实错了, 这样的程序编译都过不了. 那为何会有 Private Destructor, ...