`
dsxwjhf
  • 浏览: 70504 次
  • 性别: Icon_minigender_1
  • 来自: 安徽
社区版块
存档分类
最新评论

读书笔记 ------ 高效并发之轻量级锁

阅读更多
我们知道, Java 虚拟机堆上的对象,除了存储实体数据外,还有个对象头的概念。它存储了诸如 hashCode 、 GC 分代年龄、指向类的指针等信息。对象头中的数据大部分都不是必须的,但却能帮助虚拟机更方便、快捷地完成某些功能。比如“指向类的指针”,就可以很方便地实现反射(即使没有这个指针,虚拟机也能完成反射,但可能就会麻烦很多)。

正因为“不重要”,对象头的数据结构非常灵活,在对象处于不同状态时,根据需要存储不同的东西。比如对象未被锁定时,可以存储哈希码、 GC 分代年龄等,此时把自己的状态标志设为1;对象被轻量级锁定时,存储指向轻量级锁的指针,此时把自己的状态标志设为0;对象处于重量级锁定时,存储指向操作系统互斥量的指针,把自己的状态标志设为2,等等。

那轻量级锁又是什么呢?我们知道,为了保证共享数据的安全,我们不得不使用 synchronized 同步块之类的手段来保证共享数据的安全。但是同步通常意味着线程的阻塞,涉及到线程的挂起和唤醒,而 Java 中的线程是映射到操作系统的物理线程上的,这些挂起和唤醒操作需要程序不停地在用户态和内核态之间切换,这是一笔不小的开销。为了保证并发正确,给共享数据加锁的操作是不可避免的,但是在实际情况下,又有多少机会说好几条线程真的会在同一时间进入到相同的同步块呢?好像很少。。悲哀的是,即便只有一条线程,虚拟机还不得不执行加锁、解锁、维护锁计数器、用户态-内核态切换等等操作(好吧,只有一条线程的时候,不存在阻塞的问题,但加锁和解锁还是必须的,如果不做任何优化的话,加锁和解锁要使用操作系统的互斥量,所以还是得切换到内核态)。所以对于这种情况有没有优化的余地?轻量级锁就是为了解决这个问题被发明出来的。

轻量级锁的原理和执行过程,这里就不再抄书了。可以参考 虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁) 这篇文章,作者是周志明大大本人。

这里面有这么一句话:如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程抢占了。开始的时候很疑惑,既然 CAS 操作都失败了, Mark Word 怎么可能有指向 Lock Record 的指针呢?其实这句话是针对第二次、第三次、第 N 次加锁过程来说的,第一次加锁过程确实不会有这种情况。还有一点, CAS 操作比较的是什么?加锁的时候,比较的是对象的 Mark Word 和栈上的 Mark Word ;解锁的时候,估计是预先把刚刚加锁后的 Mark Word 记下来了,然后和当前的 Mark Word 比较。

最后再抄一点书,关于偏向锁。
  偏向锁也是 JDK 1.6 中引入的一项锁优化,它的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连 CAS 操作都不做了。
  偏向锁的“偏”,就是偏心的“偏”、偏袒的“偏”。它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。
    偏向锁在对象头中记录了获取到该锁的线程的 ID 。



补:关于 CAS 过程的修正

CAS 的语义是: CAS 有3个操作数,内存值 V ,旧的预期值 A ,要修改的新值 B 。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B ,否则什么都不做。所以,我的猜测是:

加锁过程:在 CAS 之前, Lock Record 的 owner 指针已经指向对象的 Mark Word 了。 CAS 操作判断对象当前的 Mark Word 和栈上刚刚复制过来的 Mark Word 是否相等,若相等修改对象的 Mark Word 为指向 Lock Record 的指针。若该 CAS 操作成功,则本线程成功获得锁,接着把 Object 锁标志位标记为“00”(轻量级锁定);否则检查对象 Mark Word 的指针是否指向当前线程的 Lock Record ,若是说明当前线程已经获得了锁,本次加锁是一个重入过程,可以进入同步块直接执行;否则说明该锁被其它线程抢占了(就在复制 Mark Word 和设置 owner 指针期间。可能我先开始,但别人更快),于是修改对象的锁标志为重量级锁定,并把 Mark Word 的锁指针修改为指向操作系统互斥量,对象膨胀为重量级锁,本线程进入阻塞状态。

解锁过程:和加锁类似。稍有不同的是,这次比较的是栈上 Mark Word 的地址和对象 Mark Word 指针所指向的地址是否一样。若不一样只有一种解释,别的线程在这段时间之内申请过该锁,对象膨胀成了重量锁,这时候需要唤醒被阻塞的线程。

if (header.flag == 00) {
    assign space on stack frame: Lock_Record;
	Lock_Record.header <== object.header;
	Lock_Record.pointer --> object.header;
	
	success <== CAS(object.header, Lock_Record.header, object.header.pointer --> Lock_Record and object.header.flag <== 01);
	
	if (success) {
	    OK;
	} else {
	    reenter <== object.header.pointer --> Lock_Record ??
		if (reenter) {
		    OK;
		} else {
		    object.header.flag <== 02;
			blocked;
		}
	}
} else if (header.flag == 01) {
    object.header.flag <== 02;
    blocked;
} else if (header.flag == 02) {
    blocked;
}
分享到:
评论

相关推荐

    LengendOfDong#Blog#原创-并发编程学习笔记(三)------synchronized的实现原理及应用1

    1.偏向锁 2.轻量级锁 3.锁的优缺点对比

    Java 并发编程学习笔记之Synchronized底层优化

    主要介绍了Java 并发编程学习笔记之Synchronized底层优化的相关资料,主要包含了重量级锁,轻量级锁,偏向锁和其他优化等方面,有需要的小伙伴可以参考下

    seckill-master:SSM实战项目——Java高并发秒杀API,详细流程+学习笔记

    秒杀业务场景具有典型事务特性秒杀/红包类需求越来越常见为何使用SpringMVC+Spring+MyBatis框架框架易于使用和轻量级低代码侵入性成熟的社区和用户群能从该项目得到什么收获?框架的使用和整合技巧秒杀分析过程与...

    javaee笔试题-source-address:源地址

    java ee笔试题真棒明星 我自己的真棒清单! 生成者 [Jupyter 笔记本](#jupyter 笔记本) 动作脚本 - 长期废弃的 ...和其他人维护,以确保存在安全版本的 ...轻量级的 PHP 和 C 并发 RPC 框架(即将支持 java、python

    tomcat使用笔记

    Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台...

    nginx1.8和入门笔记.zip

    Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用...

    c#学习笔记.txt

    struct 类型适合表示如点、矩形和颜色这样的轻量对象。尽管可能将一个点表示为类,但结构在某些方案中更有效。在一些情况下,结构的成本较低。例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配...

    java8集合源码-awesome-stars:我自己的真棒清单!

    java8集合源码真棒明星 我自己的真棒清单! 生成者 [Jupyter 笔记本](#jupyter 笔记本) 动作脚本 - 长期废弃的 SWFUpload 项目的一个分支,由 ...和其他人维护,以确保存在...的轻量级并发 RPC 框架(即将支持 java、pytho

    asp.net知识库

    Essential .NET 读书笔记 [第一部分] NET FrameWork的Collections支持 .NET的反射在软件设计上的应用 关于跨程序集的反射 实现C#和VB.net之间的相互转换 深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述...

    学习笔记(06):19年并发编程及原理视频培训教程入门到精通-进程与线程的区别…

    线程也被称为轻量级进程,线程是程序执行的最小单位。  一个程序至少一个进程,一给进程至少一个线程。  进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段,堆栈段和...

    net学习笔记及其他代码应用

    答:应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。 14.CTS、CLS、CLR分别作何解释? 答:CTS:通用语言系统。CLS:通用语言规范。CLR:公共语言运行库。 15.什么是装箱和拆箱? 答:从值...

    高级java笔试题-itresource:程序开发技术资源集合

    高级java笔试题 基础 [ 收藏——IO模型的4张经典...轻量级分布式 RPC 框架 ] () [ 一个简单的guava cache 例子] () [ 你应该知道的 RPC 原理 ] () [ JAVA字符串格式化-String.format()的使用 ] () [ java RMI相关反序

    python3学习之Splash的安装与实例教程

    它是一个带有HTTP API的轻量级Web浏览器,使用Twisted和QT5在Python 3中实现。QT反应器用于使服务完全异步,允许通过QT主循环利用webkit并发。 一些Splash功能: 并行处理多个网页 获取HTML源代码或截取屏幕截图 ...

    word源码java-ShangGuiGu-ZhouYang-JUC:周阳-juc

    系统在各个线程之间的切换工作要比进程负担低,因此线程又被称为轻量级进程 线程的几种状态:(见:jdk Thread类源码中的state枚举类) NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED 并发和并行: 并发是指...

    juc:Java多线程学习笔记源码部分

    线程就是轻量级进程,是程序执行的最小单位。 多进程的方式也可以实现并发,为什么我们要使用多线程? 共享资源在线程间的通信比较容易。 线程开销更小。 进程和线程的区别? 进程是一个独立的运行环境,而线程是在...

Global site tag (gtag.js) - Google Analytics