Synchronized重量级锁原理和实战(五)
在JVM中,每个对象都关联这一个监视器,这里的对象包含可Object实例和Class实例.监视器是一个同步工具,相当于一个凭证,拿到这个凭证就可以进入临界区执行操作,没有拿到凭证就只能阻塞等待.重量级锁通过监视器的方式保证了任何时间内只允许一个线程通过监视器保护的临界区代码.
重量级锁的核心原理
JVM每个对象都有一个监视器,监视器随着对象一起创建 销毁.本质上监视器是一种同步工具,也可以说是一种同步机制.
1:同步.监视器所保护的临界区代码都是互斥执行的.一个监视器一个凭证,任何一个线程执行临界区代码都需要获取凭证,执行完了释放许可.
2:协作.监视器提供Signal机制,允许正在持有许可的线程释放凭证进入阻塞等待状态,等待其他线程发送Signal去唤醒.其他拥有凭证的线程可以唤醒正在阻塞等待的线程,让它可以重新获得凭证执行临界区代码.
在虚拟机中,监视器是由C++类ObjectMonitor实现的.(我对C++不是很熟,就简单介绍下,有个印象知道如何实现,如果对技术很感兴趣,可以去学学C++)
ObjectMonitor类中关键的属性是Owner(_owner) WaitSet(_WaitSet) Cxq(_cxq) EntrlList(_EntrlList).好好品味,显示锁实现原理和这个相同.
WaitSet Cxq EntrlList说明
Cxq:竞争队列,所有请求锁的线程首先被放到竞争队列里.
EntrlList:Cxq中那些有资格成为候选资源的线程被移到EntrlList中.
WaitSet:某个拥有ObjectMonitor的线程在调用Object.wait()方法之后被阻塞,然后该线程被放置在WaitSet列表中.
ObjectMonitor内部抢锁流程
1:Cxq
Cxq并不是一个真正的队列,只是一个虚拟队列,原因在于Cxq是由Node及其next指针逻辑构成,并不存在一个队列数据结构,(我自己的理解是引用,就好比1引用2,2引用3,以此类推).
每次新加入Node会在Cxq的队头进行,通过CAS改变第一个节点的指针为新增节点.同时新增节点的next指针指向后续节点.从Cxq取元素时,会从队尾获取.可以看出来,Cxq是一个无锁结构.
因为只有Owner线程才能从队尾获取元素,所以线程出队没有竞争,也避免了ABA问题.还有线程在进入Cxq之前,会通过CAS操作进行一次抢锁,获取不到才会进入队列,所以重量级锁是一个非公平锁.
2:EntrlList
EntrlList与Cxq在逻辑上都属于等待队列.Cxq会被线程并发访问为了降低对Cxq队尾的竞争,而建立了EntrlList,在Owner线程释放锁的时候,JVM会从Cxq中迁移线程到EntrlList,并会指定EntrlList中的某个线程(一般为头线程)为OnDeck Thread(Ready Thread).EntrlList里的线程作为候选者线程存在.
3:OnDeck Thread 与 Owner Thread
JVM不直接把锁传递给Owner Thread,而是把锁竞争的权利交给OnDeck Thread,OnDeck需要重新竞争锁,这样虽然牺牲了一定的公平性,但是提高了吞吐量.在JVM中也把这种行为叫做竞争切换.
OnDeck Thread线程获取到锁后会变为Owner Thread,无法获取锁的OnDeck Thread依然会留在EntrlList中,考虑到公平性,OnDeck Thread在队列中的位置不会变.
在OnDeck Thread成为Owner Thread的过程中还有一个不公平的事情,就是后来新抢锁的线程有可能直接获取锁成为WaitSet.
4:WaitSet
如果Owner Thread调用了Object.wait()方法之后就会进入WaitSet队列.直到某个时刻调用Object.notify()方法或者Object.notifyAll()唤醒,线程会重新进入EntrlList队列.
重量级锁开销
处于ContentionList EntrlList WaitSet中的线程都处于阻塞状态.线程的阻塞和唤醒都需要操作系统来帮忙.Linux内核下采用pthread_mutex_lock系统调用实现,进程要从用户态切换到内核态.
Linux系统的体系分为用户态和内核态.
Linux系统的内核是一组特殊的软件程序,负责控制计算机硬件资源.例如协调CPU资源,分配内存资源,并且提供稳定的环境供程序运行.应用程序的活动空间为用户空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源 存储资源 IO资源等.
用户态与内核态有各自专用的内存空间 专用的寄存器等.进程从用户态切换至内核态需要传递许多变量 参数给内核,内核也需要保护好用户态在切换时的一些寄存器值 变量等.以便内核态调用结束后可以切换回用户态继续工作.
用户态的进程能够访问的资源受到了极大的控制,而运行在内核态的进程可以为所欲为.一个进程可以运行在用户态也可以运行在内核态,它们之间肯定有切换的方式.
用户态切换内核态的方式
1:硬件中断.硬件中断也称为外设中断,当外设完成用户请求时,会向CPU发送中断信号.
2:系统调用.其实系统调用本身就是中断,只不过是软件中断,与硬件中断不同.
3:异常.如果当前进程运行在用户态,这时发生了异常事件(例如缺页异常),就会触发切换.
用户态是应用程序运行的空间,为了能访问到内核管理的资源(例如CPU 内存 IO),可以通过内核态所提供的的访问接口实现,这些接口叫做系统调用.pthread_mutex_lock系统调用是内核态为用户态提供的Linux内核态下互斥锁的访问机制,所以使用pthread_mutex_lock系统调用时,进程需要从用户态切换到内核态,这种切换要消耗很多的时间,有可能比用户执行代码的时间还长.
重量级锁演示
public class HeavyWeightLockTest {static final int MAX_TURN = 1000;public static void main(String[] args) throws InterruptedException {System.out.println(VM.current().details());//JVM偏向锁.Thread.sleep(5000);ObjectLock objectLock = new ObjectLock();//抢锁前状态.System.out.println("抢锁前objectLock状态:");objectLock.printObjectStruct();Thread.sleep(5000);CountDownLatch latch = new CountDownLatch(3);Runnable runnable = () -> {for (int i = 0; i < MAX_TURN; i++) {synchronized (objectLock) {objectLock.increase();if (i == 0) {System.out.println("第一个线程抢锁,lock的状态为:");objectLock.printObjectStruct();}}}latch.countDown();for (int j = 0; ; j++) {LockSupport.parkNanos(10);}};new Thread(runnable).start();LockSupport.parkNanos(2000);Runnable lightWeightRunnable = () -> {for (int i = 0; i < MAX_TURN; i++) {synchronized (objectLock) {objectLock.increase();if (i == 0) {System.out.println(Thread.currentThread().getName()+ "占有锁,lock的状态为:");objectLock.printObjectStruct();}LockSupport.parkNanos(10);}}latch.countDown();};//启动两个线程开始抢锁.new Thread(lightWeightRunnable, "抢锁线程-1").start();Thread.sleep(5000);new Thread(lightWeightRunnable, "抢锁线程-2").start();latch.await();LockSupport.parkNanos(2000);System.out.println("释放锁,lock的状态为:");objectLock.printObjectStruct();}
}
可以看出lock标记位为偏向锁状态,还没有偏向线程.
可以看出lock标记位101为偏向状态,并且有了偏向线程.
可以看出lock标记位000已经不是偏向锁,升级为了轻量级锁.
可以看出lock标记位变为了010成为了重量级锁.
我一直在努力,被质疑,被嘲讽.在沮丧,还是会在第二天早晨一如既往的去战斗.我坚信我会成功.
如果大家喜欢我的分享的话,可以关注下我的微信公众号
心有九月星辰
相关文章:

Synchronized重量级锁原理和实战(五)
在JVM中,每个对象都关联这一个监视器,这里的对象包含可Object实例和Class实例.监视器是一个同步工具,相当于一个凭证,拿到这个凭证就可以进入临界区执行操作,没有拿到凭证就只能阻塞等待.重量级锁通过监视器的方式保证了任何时间内只允许一个线程通过监视器保护的临界区代码. …...

linux常用网络工具汇总三
linux常用网络工具汇总 6. 抓包工具6.1 wireshark安装界面介绍使用过滤器TCP协议示例关于wireshark的缺点 6.2 tcpdump命令格式关键字使用关于tcpdump的缺点 6.3 fiddler6.4 burpsuite 6. 抓包工具 6.1 wireshark Wireshark(前称Ethereal)是一个网络封…...

Linux中nano编辑器详解
nano 是一个简单的文本编辑器,通常预装在大多数 Linux 发行版中。它非常适合初学者使用,因为它有一个用户友好的界面和易于理解的命令集。下面是对 nano 编辑器的详细说明。 启动 nano 要启动 nano 并打开一个文件进行编辑,你可以在终端中输…...

26-vector arraylist和linkedlist的区别
Vector, ArrayList, 和 LinkedList 是Java中常见的三种列表实现,它们各自具有不同的特点和适用场景。 同步性与线程安全: Vector 是同步的,即线程安全的,它的所有方法都是同步的,可以由两个线程安全地访问…...

20-redis穿透击穿雪崩
Redis中的缓存穿透、缓存击穿和缓存雪崩是三种常见的缓存问题: 缓存穿透:指缓存和数据库中都没有的数据,但用户还是源源不断地发起请求,导致每次请求都会直接访问数据库,从而可能压垮数据库。缓存击穿&…...

Docker使用教程
Docker 名词解释 镜像(image):Docker镜像就是一个模板,可以通过这个模板来创建容器服务。容器(container):Docker利用容器技术,独立运行一个或者一组应用,通过镜像创建…...

poi-tl循环放图片+文字说明
这几天有个任务,服务端导出word要求从数据库取到多张图片,然后输出到word中,并且说明一共几张,当前是第几张。 网上翻了很久也没有找到示例,不过最终难题还是得到了攻克。 因为之前的代码是有一个导出的map,…...

数据结构之树的存储结构
一、顺序存储结构 顺序存储结构通常用于表示完全二叉树。在这种存储方式中,树中的节点被存储在一个连续的数组中。对于完全二叉树,如果父节点的索引是i(假设从0开始计数),那么它的左子节点的索引是2i1,右子…...

Zotero 常用插件介绍
1. Zotero 插件安装方法 下载以 .xpi 结尾的插件;打开 Zotero → 工具 → 插件 → 右上小齿轮图标 → Install Add-on From File ... → 选择下载好的 .xpi 插件安装 → 重启 Zotero 2. 常用插件介绍 2.1. Scholaread - 靠岸学术 Zotero 英文文献相关插件…...

WebSocket协议解析
文章目录 一、HTTP协议与HTTPS协议1.HTTP协议的用处2.HTTP协议的特点3.HTTP协议的工作流程4.HTTPS协议的用处5.HTTPS协议的特点6.HTTPS协议的工作流程 二、WebSocket协议出现的原因1. 传统的HTTP请求-响应模型2. 轮询(Polling)3. 长轮询(Long…...

ES6 (一)——ES6 简介及环境搭建
目录 简介 环境搭建 可以在 Node.js 环境中运行 ES6 webpack 入口 (entry) loader 插件 (plugins) 利用 webpack 搭建应用 gulp 如何使用? 简介 ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版…...

HarmonyOS开发案例:列表场景实例-TaskPool
介绍 本实例通过列表场景实例讲解,介绍在TaskPool线程中操作关系型数据库的方法,涵盖单条插入、批量插入、删除和查询操作。 效果图预览 使用说明 进入页面有insert(单条数据插入)、batch insert(批量数据插入)、query(查询操作)三个按钮,…...

谷歌浏览器如何隐藏书签
谷歌浏览器的书签栏是一个极为方便的功能,它能够帮助用户快速访问自己频繁使用的网页。然而,有些时候为了保护个人隐私或使浏览界面更为简洁,我们可能需要隐藏书签栏。接下来就为大家分享如何隐藏谷歌浏览器的书签栏,一起来看看吧…...

SQL - 视图
我们可以把查询或子查询存到视图里,视图的作用就像一张虚拟表,再次查询时,就不需要再写一次复杂的查询。创建视图 create view 视图名 as (查询); create or replace view clients_balance as (查询); create or replace view clients_balanc…...

centos7环境升级默认的gcc 4.8.5到gcc 8.2.0, 并且升级glibc到glibc 2.28
这里写目录标题 makegccglibc make #下载 wget http://ftp.gnu.org/gnu/make/make-4.2.tar.gz tar -xf make-4.2.tar.gz cd make-4.2 ./configure make -j4 make install mv /usr/bin/make /usr/bin/make_bak cp ./make /usr/bin/make -v GNU Make 4.2 Built for x86_64-pc-li…...

FastHTML:使用 Python 彻底改变 Web 开发
什么是 FastHTML?🌐 FastHTML 是一个现代 Python Web 应用程序框架,其真正目的是让 Python 开发人员轻松进行 Web 开发。它大大减少了对 JavaScript 和 CSS 构建交互式和可扩展 Web 应用程序的依赖。FastHTML 通过使用 Python 对象来表示 HTM…...

快速排序的深入优化探讨
快排性能的关键点分析 决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本⼆分居中,那么快排的递归树就是颗均匀的满⼆叉树,性能最佳。但是实践中虽然不可能每次都是⼆分居中,但是性能也还是可…...

c语言杂谈系列:模拟虚函数
从整体来看,笔者的做法与之前的模拟多态十分相似,毕竟c多态的实现与虚函数密切相关 废话少说,see my code: kernel.c#include "kernel.h" #include <stdio.h>void shape_draw(struct shape_t* obj) {/* Call dr…...

短视频推广App不再难!Xinstall来帮忙
在短视频风靡的今天,如何利用这一热门媒介有效推广App,成为了许多推广者关注的焦点。而Xinstall,作为国内专业的App全渠道统计服务商,正是你解决这一难题的得力助手。 首先,Xinstall在数据维度上的优势无可比拟。它能…...

打靶记录13——doubletrouble
靶机: https://www.vulnhub.com/entry/doubletrouble-1,743/ 难度: 中 目标: 取得两台靶机 root 权限 涉及攻击方法: 主机发现端口扫描Web信息收集开源CMS漏洞利用隐写术密码爆破GTFObins提权SQL盲注脏牛提权 学习记录&am…...

awk文本处理工具
awk 是一个强大的文本处理工具,在Shell编程中常用于处理和分析文本数据。它可以按列处理数据,进行模式匹配,生成报告,执行计算等。以下是一些 awk 的主要功能和使用场景: 期待您的关注 美好的观念较美人尤为可爱 目录 …...

计算机毕业设计选题推荐-学院网站系统-Java/Python项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

Spring模块详解Ⅰ
目录 SpringSpring框架的主要功能模块1. Core Container(核心容器)2. Data Access/Integration(数据访问与集成)3. Web4. AOP (Aspect-Oriented Programming,面向切面编程)5. Instrumentation(工具集&#…...

C语言程序设计-练习篇
山海自有归期,风雨自有相逢。 一 下面代码的结果是什么? int main() { int i 0; for (i 0; i < 10; i) { if (i 5) //此处为赋值,i 5表达式结果为5 printf("%d ", i); //表达式为真&a…...

【Oracle篇】统计信息和动态采样的深度剖析(第一篇,总共六篇)
💫《博主介绍》:✨又是一天没白过,我是奈斯,DBA一名✨ 💫《擅长领域》:✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️…...

无源互调自动化测试软件应用案例分享:S参数和互调的高效测试
随着产品种类的丰富和市场需求的变化,合肥某电子技术公司意识到,传统的手工测试已无法满足公司持续发展的需要。于是,一场自动化测试转型悄然展开。 一、背景介绍 合肥某电子技术公司成立于2009年,专注于功分器、耦合器、负载器、…...

【6大设计原则】精通设计模式之里氏代换原则:从理论到实践,掌握代码演化的黄金法则
一、引言 1.1 设计模式的必要性 在软件开发的复杂性面前,设计模式提供了一套成熟的解决方案,它们是经过多年实践总结出来的,能够帮助我们应对各种编程难题。设计模式不仅仅是一种编程技巧,更是一种编程哲学,它能够提…...

国内服务器安装Docker提示Failed to connect to download.docker.com port 443的解决方案
解决方案 换国内镜像源。我用的是清华的。https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ 自己找自己对应的版本。 例如你的Ubuntu系统。就用下列命令 sudo curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/do…...

前端开发攻略---彻底弄懂跨域解决方案
目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…...

【HeadFirst 设计模式】装饰者模式的C++实现
一、案例背景 Starbuzz是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店,在对面街上肯定还会看到另一家。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的…… 购买咖啡时&am…...