深入Java内存模型之阅读理解(2)
锁的释放-获取建立的happens before 关系
锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。
下面是锁释放-获取的示例代码:
class MonitorExample {
int a = 0;
public synchronized void writer() { //1
a++; //2
} //3
public synchronized void reader() { //4
int i = a; //5
……
} //6
}
假设线程A执行writer()方法,随后线程B执行reader()方法。根据happens before规则,这个过程包含的happens before 关系可以分为两类:
- 根据程序次序规则,1 happens before 2, 2 happens before 3; 4 happens before 5, 5 happens before 6。
- 根据监视器锁规则,3 happens before 4。
- 根据happens before 的传递性,2 happens before 5。
上述happens before 关系的图形化表现形式如下:

在上图中,每一个箭头链接的两个节点,代表了一个happens before 关系。黑色箭头表示程序顺序规则;橙色箭头表示监视器锁规则;蓝色箭头表示组合这些规则后提供的happens before保证。
上图表示在线程A释放了锁之后,随后线程B获取同一个锁。在上图中,2 happens before 5。因此,线程A在释放锁之前所有可见的共享变量,在线程B获取同一个锁之后,将立刻变得对B线程可见。
锁释放和获取的内存语义
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。以上面的MonitorExample程序为例,A线程释放锁后,共享数据的状态示意图如下:

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

对比锁释放-获取的内存语义与volatile写-读的内存语义,可以看出:锁释放与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。
下面对锁释放和锁获取的内存语义做个总结:
- 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
- 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
- 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。
ReentrantLock的实现:
class ReentrantLockExample {
int a = 0;
ReentrantLock lock = new ReentrantLock();
public void writer() {
lock.lock(); //获取锁
try {
a++;
} finally {
lock.unlock(); //释放锁
}
}
public void reader () {
lock.lock(); //获取锁
try {
int i = a;
……
} finally {
lock.unlock(); //释放锁
}
}
}
在ReentrantLock中,调用lock()方法获取锁;调用unlock()方法释放锁。
ReentrantLock的实现依赖于java同步器框架AbstractQueuedSynchronizer(简称之为AQS)。
AQS使用一个整型的volatile变量(命名为state)来维护同步状态。
深入Java内存模型之阅读理解(2)的更多相关文章
- 深入Java内存模型之阅读理解(1)
Java内存模型的抽象 在java中,所有实例域.静态域和数组元素存储在堆内存中,堆内存在线程之间共享(本文使用“共享变量”这个术语代指实例域,静态域和数组元素).局部变量(Local variabl ...
- java内存模型原理阅读总结
Java内存模型可以理解为在特定操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象.不同架构的物理计算机可以有不一样的内存模型,java虚拟机也有自己的内存模型,java虚拟机规范中试图定义一种 ...
- java内存模型:简单理解
1.Java内存模型(Java Memory Model,JMM) 2.JMM定义了线程和主内存之间的抽象关系: 线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地 ...
- Java内存模型的深入理解
基础 并发编程的模型分类 在并发编程需要处理的两个关键问题是:线程之间如何通信 和 线程之间如何同步. 通信 通信 是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存 ...
- 《成神之路-基础篇》JVM——Java内存模型(已完结)
Java内存模型 本文是<成神之路系列文章>的第一篇,主要是关于JVM的一些介绍. 持续更新中 Java内存模型 JVM内存结构 VS Java内存模型 VS Java对象模型(Holli ...
- 《Java并发编程实战》第十六章 Java内存模型 读书笔记
Java内存模型是保障多线程安全的根基,这里不过认识型的理解总结并未深入研究. 一.什么是内存模型,为什么须要它 Java内存模型(Java Memory Model)并发相关的安全公布,同步策略的规 ...
- Java并发(二):Java内存模型
一.硬件内存架构 一个现代计算机通常由两个或者多个CPU.其中一些CPU还有多核.每个CPU在某一时刻运行一个线程是没有问题的.如果你的Java程序是多线程的,在你的Java程序中每个CPU上一个线程 ...
- 并发编程-Java内存模型
将之前看过的关于并发编程的东西总结记录一下,本文简单记录Java内存模型的相关知识. 1. 并发编程两个关键问题 并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步. (1)在命令式 ...
- Java并发编程:Java内存模型JMM
简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...
随机推荐
- 与TIME_WAIT相关的几个内核参数修改测试讨论结论
以下来结论自tcpcopy & gryphon讨论群 经过试验测试得出,不保证肯定正确. net.ipv4.tcp_tw_recycle net.ipv4.tcp_tw_reuse net ...
- springboot2.X 集成redis+消息发布订阅
需求场景:分布式项目中,每个子项目有各自的 user 数据库, 在综合管理系统中存放这所有用户信息, 为了保持综合管理系统用户的完整性, 子系统添加用户后将用户信息以json格式保存至redis,然后 ...
- Win7下MongoDB的安装和使用
Win7下MongoDB的安装和使用 1.下载: http://www.mongodb.org/downloads 2.安装: 安装目录为 D:\mongodb\MongoDB 2.6 Standar ...
- 01-复杂度1 最大子列和问题(剑指offer和PAT)
01-复杂度1 最大子列和问题 (20分) 给定KK个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., N ...
- Machine、Swarm、Compose、SocketPlane这些Docker生态圈软件各解决了哪些问题?
Machine:解决的是操作系统异构安装Docker困难的问题,没有Machine的时候,CentOS是一种,Ubuntu又是一种,AWS又是一种.有了Machine,所有的系统都是一样的安装方式. ...
- 《Unix&Linux大学教程》学习笔记5 :正则表达式
1:Unix下正则表达式规则
- Hadoop2.2.0分布式安装配置详解[2/3]
前言 本文主要通过对hadoop2.2.0集群配置的过程加以梳理,所有的步骤都是通过自己实际测试.文档的结构也是根据自己的实际情况而定,同时也会加入自己在实际过程遇到的问题.搭建环境过程不重要,重要点 ...
- o2o的关健在于线下!
其实,说到底,O2O不是那么简单的.一堆空降兵和一群自以为是的风投都把所有的线下产业想简单了.没有真正的去了解和体验这个产业,就动不动一个小点子,创新一个小平台就妄想能改变或替代中国某某产业.看看这几 ...
- Django的AbstractUser的几大步骤
- 导入 AbstractUser from django.contrib.auth.models import AbstractUser - 往其中添加列,即可在Django的基础上添加我们所需要的 ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...