JDK 源码阅读 —— Java 内存模型

零. 为什么需要 Java 内存模型

为了让程序员忽略掉各种硬件和操作系统的内存访问差异, 也既无需关心不同架构上内存模型的差异, Java 在代码和硬件内存模型间又提供了一个 Java 内存模型。

一. 并发模型的分类

在并发编程中,需要处理两个关键问题:线程之间如何通信(线程之间以何种机制来交换信息, 有两种方式:共享内存和消息传递)及线程之间如何同步。

在共享内存的并发模型里(如 Java),线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态(主存)来隐式(对程序员透明)进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。

同步是指程序用于控制不同线程之间操作发生相对顺序的机制。在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。

虽然 Java 是共享内存并发模型, 对程序员透明, 但是不理解 Java 内存模型, 就会对内存可见性, 有序性等问题出现时找不到解决方法。

二. Java 虚拟机运行时数据区

虚拟机栈描述的是 Java 执行的内存模型: 每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、  操作数栈、 动态链接、 方法出口等信息。

Java 内存模型的主要目标是定义程序中各个变量的访问规则, 既在虚拟机中对变量的读写操作, 监视器的加锁和释放操作, 以及线程的启动和合并操作。

三. 原子性、 可见性和有序性

Java 内存模型是围绕着在并发过程中如何处理原子性、 可见性和有序性来建立的。

原子性: 操作不可再分。 如在 Java 代码中使用 synchronized 和 ReentrantLock 来保障。

可见性: A 线程操作, 对 B 线程可见, 既 A 变量赋值 1, B 线程能看见变量已经变为 1。 synchronized 、final、 volatile 和 ReentrantLock 都能提供。

有序性: 在本线程内观察, 所有操作都是有序的(线程内表现为串行语义); 如果在另一个线程中观察另一个线程, 所有操作都是无序的(指令重排序与工作内存与主内存同步延迟)。synchronized 、 volatile 和 ReentrantLock 都能提供有序性保证。

从上述可以得出 synchronized 和 ReentrantLock 是万能的, 但是效率却有巨大的差别, ReentrantLock 会比 synchronized 好很多, ConcurrentHashMap 的源码实现中就利用了 ReentrantLock 分段锁来提升并发安全和效率。

四. Happens-Before 规则

要保证 A 线程看到 B线程的操作结果(无论 A 和 B 是否在同一线程中执行), 那么 A 和 B 之间必须满足 Happens-Before 关系。

Happens-Before 规则如下:

程序次序规则(Program Order Rule):程序中 操作 A 在操作 B 之前, 那么在线程中 A 操作将在 B 操作之前进行。

管程锁定规则(Monitor Lock Rule):一个 unlock 操作 happen—before 后面(时间上的先后顺序,下同)对同一个锁的 lock 操作。

volatile 变量规则:对一个 volatile 变量的写操作 happen—before 后面对该变量的读操作。

线程启动规则:Thread 对象的 start() 方法 happen—before 此线程的每一个动作。

线程终止规则:线程的所有操作都 happen—before 对此线程的终止检测,可以通过 Thread.join() 方法结束、Thread.isAlive() 的返回值等手段检测到线程已经终止执行。

线程中断规则:对线程 interrupt() 方法的调用 happen—before 发生于被中断线程的代码检测到中断时事件的发生。

终结器规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)happen—before 它的 finalize() 方法的开始。

传递性:如果操作A happen—before 操作 B,操作 B happen—before 操作 C,那么可以得出 A happen—before 操作 C。

如果两个操作间缺乏 Happens-Before 关系, 那么 JVM 可以对它们进行任意排序。

五. 参考资料

《Java 并发编程实战》

《深入理解 Java 虚拟机》

http://www.infoq.com/cn/articles/java-memory-model-1

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-12 21:03:14

JDK 源码阅读 —— Java 内存模型的相关文章

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *

JDK 源码 阅读 - 2 - 设计模式 - 创建型模式

A.创建型模式 抽象工厂(Abstract Factory) javax.xml.parsers.DocumentBuilderFactory DocumentBuilderFactory通过FactoryFinder实例化具体的Factory. 使用例子: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilder

JDK源码阅读(一):Object源码分析

最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 hashCode 方法 toString 方法 finalize 方法 registerNatives 方法 1. Object结构图 2. 类构造器 ??类构造器是创建Java对象的方法之一.一般我们都使用new关键字来进行实例,还可以在构造器中进行相应的初始化操作. ??在一个Java类中必须存在一个

JDK源码阅读(三):ArraryList源码解析

今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 一般来讲文章开始应该先介绍一下说下简介.这里就不介绍了 如果你不知道ArrayList是什么的话就没必要在看了.大致讲一下一些常用的方法 2.继承结构 ArrayList源码定义: ArrayList继承结构如下: Serializable 序列化接口 Cloneable 前面我们在看Object源

jdk源码阅读-HashMap

前置阅读: jdk源码阅读-Map : http://www.cnblogs.com/ccode/p/4645683.html 在前置阅读的文章里,已经提到HashMap是基于Hash表实现的,所以在讲解HashMap之前 ,有必要提前了解下Hash的原理. 参考<算法导论><算法>

jdk源码阅读笔记之java集合框架(一)(基础篇)

结合<jdk源码>与<thinking in java>,对java集合框架做一些简要分析(本着实用主义,精简主义,遂只会挑出个人认为是高潮的部分). 先上一张java集合框架的简图: 会从以下几个方面来进行分析: java 数组; ArrayList,LinkedList与Vector; HashMap; HashSet 关于数组array: 数组的解释是:存储固定大小的同类型元素.由于是"固定大小",所以对于未知数目的元素存储就显得力不从心,遂有了集合.

JDK源码笔记-java.util.HashMap

HashMap 的存储实现 当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例: Java代码 HashMap<String , Double> map = new HashMap<String , Double>(); map.put("语文" , 80.0); map.put("数学" , 89.0); map.put("英语" , 78.2); HashMap 采用一种所谓的&quo

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

jdk 源码阅读有感(一)String

闲暇之余阅读 jdk 源码. (一)核心属性 /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 String的核心结构,char型数组与 int 型 hash值. (二)构造器 构造器方面,由于上述两个值是不可更改的,所以直接 对 String