JAVA多线程编程——JAVA内存模型
一、何为“内存模型”
内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,但是编译器、运行库、处理器或者系统缓存可以有特权在变量指定内存位置存储或者取出变量的值。
二、JMM(Java Memory Model)即Java内存模型的作用
- JMM的最初目的是为了能够支持多线程程序。JMM使得每一个线程就像运行在不同的机器、不同的CPU或者本身就不同的线程上一样;
- JMM定义了Java语言针对内存的一系列相关规则。对于CPU本身而言,一个CPU不能直接访问其它CPU的寄存器,因此JMM必须通过某种定义规则来使得线程和线程在工作内存中进行相互调用,从而实现一个CPU对其它CPU、或者说一个线程对其它线程的内存中资源的访问;
- 虽然JMM设计之初是为了能够更好地支持多线程,但是JMM的应用和实现并不局限于多处理器,对于单CPU的系统而言,在JVM编译器编译Java程序的时候,以及运行时执行该程序的时候,这种规则也是有效的;
- JMM定义了线程与主存之间的抽象关系:每个线程可以被抽象为一块工作内存,程序中所有的共享变量都在主存中定义并存储,工作内存不能直接使用主存中的共享变量,如果要使用,工作内存必须对主存中的共享变量进行读取和拷贝,然后对拷贝过来的变量副本进行操作,最后将操作后的变量结果回写到主存中。大多数JMM规则在实现的时候,必须保证主存和工作内存之间进行通信,而且不能违反内存模型本身的结构。这是在设计语言的时候必须考虑到的针对内存的一种设计方法。
作为Java程序员,我们需要知道的是,Java对内存的管理不需要人为操作,因为Java本身就拥有了一套自动的内存管理策略,这是Java相对与其它一些语言在进行内存管理上具备的一种优势。
三、线程间通信机制
在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。
- 共享内存。线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信;
- 消息传递。线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。
Java在实现线程间通信时采用的是共享内存的方式,因而Java线程之间的通信总是隐式的,整个通信过程对程序员完全透明。如果我们在编写多线程程序的时候不理解这种隐式的通信机制,很可能会遇到各种奇怪的并发问题。
四、主存与工作内存
上面我们将每个单独的线程抽象为一块工作内存,主存与线程之间的关系也就被抽象成了主存与工作内存的关系,这种关系用图可表示为:
JMM定义了8中主存与工作内存之间的操作:
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态;
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用;
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作;
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作;
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b, load a。
JMM还规定了在执行上述八种基本操作时,必须满足如下规则:
- 不允许read和load、store和write操作之一单独出现;
- 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中;
- 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中;
- 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作;
- 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现;
- 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值;
- 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量;
- 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。
五、关于重排序
在Java程序的执行过程中,编译器和处理器会通过对指令进行重排序来优化程序的执行效率。重排序分为三种:
1.编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序;
2.指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;
3.内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
从java源代码到最终实际执行的指令序列,会分别经历上面三种重排序。
JAVA多线程编程——JAVA内存模型的更多相关文章
- Java并发编程、内存模型与Volatile
http://www.importnew.com/24082.html volatile关键字 http://www.importnew.com/16142.html ConcurrentHash ...
- Java多线程中的内存模型
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6536131.html 一:现代计算机的高速缓存 在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的 ...
- [心得笔记]Java多线程中的内存模型
一:现代计算机的高速缓存 在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的读写速度 与 处理器运算速度,在CPU和内存设备之间加入了一个名为Cache的高速缓存设备来作为缓冲:将运算需要 ...
- 初识Java多线程编程
Java 多线程编程 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别 ...
- Java - 32 Java 多线程编程
Java 多线程编程 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别 ...
- Java-Runoob-高级教程: Java 多线程编程
ylbtech-Java-Runoob-高级教程: Java 多线程编程 1.返回顶部 1. Java 多线程编程 Java 给多线程编程提供了内置的支持. 一条线程指的是进程中一个单一顺序的控制流, ...
- Java 学习(19):Java 多线程编程
Java 多线程编程 Java 给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特 ...
- Java并发编程-Java内存模型
JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...
- Java多线程编程实战02:多线程编程模型
多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...
随机推荐
- posix 正则库程序
使用的是posix 正则库,参考: http://see.xidian.edu.cn/cpp/html/1428.html 执行匹配的时: gcc myreg.c ip.pat 内容: ip.*[0- ...
- 在ubuntu下如何验证文件的MD5码 (转载)
转自:http://blog.csdn.net/david_xtd/article/details/7641682 在windows下可以使用专用的工具软件如WinMD5等来查看文件的MD5码, 在u ...
- Go语言入门——数组、切片和映射
按照以往开一些专题的风格,第一篇一般都是“从HelloWorld开始” 但是对于Go,思来想去,感觉真的从“HelloWorld”说起,压根撑不住一篇的篇幅,因为Go的HelloWorld太简单了. ...
- MySQL学习中,遇到的问题记录
一.安装mysql时,报错:1045 原因:因为之前安装过mysql,之后再安装会出现这个问题. 解决: 删除之前的残留就好了 方法:清除mysql之前的历史残留
- poj2449(k短路&A_star模板)
题目链接:http://poj.org/problem?id=2449 题意:给出一个有向图,求s到t的第k短路: 思路:k短路模板题,可以用A_star模板过: 单源点最短路径+高级搜索A*;A*算 ...
- 洛谷P2018 消息传递
P2018 消息传递 题目描述 巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级.如果A是B的上级,B是C的上级,那么A就是C的上级.绝对不会出现这样的关系:A是B的 ...
- JDK 简介
JDK简介 JDK java开发工具包 JRE java 运行时环境 JVM java虚拟机 三者的关系:JDK 包含 JRE,JRE 包含 JVM Java的核心优势是跨平台,由JVM虚拟机实现的. ...
- Python-8-print和import进阶
1.打印多个参数 用逗号隔开: >>> print('Age:', 42) Age: 42 参数之间自动插入了一个空格字符 >>> name = 'Gumby' & ...
- CC08:翻转子串
题目 假定我们都知道非常高效的算法来检查一个单词是否为其他字符串的子串.请将这个算法编写成一个函数,给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成,要求只能调用一次检查子串的函数. 给 ...
- rand()函数的用法
C++中rand() 函数的用法 1.rand()不需要参数,它会返回一个从0到最大随机数的任意整数,最大随机数的大小通常是固定的一个大整数. 2.如果你要产生0~99这100个整数中的一个随机整数, ...