【JUC】三十二、邮戳锁StampedLock
文章目录
- 1、邮戳锁
- 2、锁饥饿问题的解决思路
- 3、邮戳锁的特点
- 4、代码演示:邮戳锁的传统读写用法
- 5、代码演示:邮戳锁之乐观读
- 6、邮戳锁的缺点
- 7、终章回顾
前面提到了从无锁 ⇒ 独占锁 ⇒ 读写锁,但读写锁存在写锁饥饿的情况。
- 📕【读写锁的演化与锁降级】
本篇邮戳锁(也称版本锁、票据锁)即是对读写锁的再一次演化。
1、邮戳锁
StampedLock是JDK1.8中新增的一个读写锁,也是对JDK1.5中的读写锁ReentrantReadWriteLock的优化。
stamp,戳记,long类型,代表了锁的状态,当stamp返回0时,表示线程获取锁失败,且当释放锁或者转换锁时,都要传入最初获取的stamp值。
2、锁饥饿问题的解决思路
idea1:使用公平锁可一定程度上缓解锁饥饿问题,但这样是以牺牲系统吞吐量为代价的
new ReentrantReadWriteLock(true);
idea2:Java8的StampedLock类的乐观读锁
读写锁的实现类ReentrantReadWriteLock是读写互斥,写写互斥,但读读共享,因此性能比synchronized等独占锁好很多。而其读写互斥的特点,在读线程多,写线程少时,会导致写锁饥饿,基于此,邮戳锁提供乐观锁。
获取乐观锁后,其他线程尝试获取写锁时不再会被阻塞,同时,乐观读后也要对结果进行校验。很乐观,认为我读的时候,不会有人改,如此,相比ReentrantReadWriteLock读写锁,性能又上了一个台阶。
对短的只读代码段,使用乐观模式通常可以减少争用并提高吞吐量 (强调短的读,是因为读的时间短了,中间被写线程改数据的概率就低,更容易乐观成功)
3、邮戳锁的特点
- 所有获取锁的方法,都返回一个邮戳Stamp,Stamp为零表示锁获取失败,其余都表示成功
- 所有释放锁的方法,都需要一个邮戳Stamp,这个Stamp必须是和成功获取锁时得到的stamp一致
- StampedLock是不可重入的,因此,如果一个线程已经持有了写锁,又再去获取写锁的话就会造成死锁
StampedLock有三种访问模式:
- Reading(读模式悲观):功能和ReentrantReadWriteLock的读锁类似
- Writing(写模式):功能和ReentrantRerdWriteLock的写锁类似
- Optimistic reading (乐观读模式):无锁机制,类似于数据库中的乐观锁,
支持读写并发
,很乐观的认为读取时没人修改,假如被修改,再升级为悲观读模式
4、代码演示:邮戳锁的传统读写用法
以下演示,邮戳锁也可以当作传统的读写锁来使用:
//资源类
public class ShareSource {int number = 6;StampedLock stampedLock = new StampedLock();/*** 写*/public void writer(){long stamp = stampedLock.writeLock();System.out.println(Thread.currentThread().getName() + "写线程准备修改共享资源");try{number = number + 1;}finally {stampedLock.unlockWrite(stamp);}System.out.println(Thread.currentThread().getName() + "写线程修改结束");}/*** 悲观读,没读完时,写锁无法获取* 读的过程中停几秒,以明显看到是否允许写锁进入*/public void read(){long stamp = stampedLock.readLock();System.out.println(Thread.currentThread().getName() + " 进入读锁,预计4秒后读取完成");for (int i = 1; i < 5; i++) {try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace();}NumberFormat nf = NumberFormat.getPercentInstance();nf.setMaximumFractionDigits(2); //最大小数位数System.out.println(Thread.currentThread().getName() + "读取进度" + nf.format(i/4.00));}try{int result = number;System.out.println(Thread.currentThread().getName() + "读取完成,获得共享对象变量值为:" + result);}finally {stampedLock.unlockRead(stamp);}}
}
测试类:
public class Test {public static void main(String[] args) throws InterruptedException {ShareSource shareSource = new ShareSource();new Thread(() -> {shareSource.read();}, "readThread").start();//为了测试效果,确保读线程先启动TimeUnit.MILLISECONDS.sleep(100);new Thread(() -> {shareSource.writer();}, "writeThread").start();//这里不用JUC辅助类了,直接sleep等着TimeUnit.SECONDS.sleep(6);System.out.println(Thread.currentThread().getName() + "查看最终结果number: " + shareSource.number);}
}
运行,和普通的读写锁没什么区别,没读完前,写线程不让进,依旧读写互斥:
5、代码演示:邮戳锁之乐观读
下面用邮戳锁的乐观读,演示读的过程也允许写锁介入。先看乐观读的校验方法:
//返回true,代表中途没被其他线程修改public boolean validate(long stamp)
Demo修改,写资源类的乐观读的业务方法:
//资源类
/*** 乐观读* 读的过程中允许写锁的获取*/
public void optimisticRead(){long stamp = stampedLock.tryOptimisticRead();int result = number;//故意间隔4秒,看中间有没其他线程修改过numberfor (int i = 1; i < 5; i++) {try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在读取中,"+ i + "秒后,validate方法值(true无修改,false有修改):" + stampedLock.validate(stamp));}//读完了,校验下中途有没被写线程修改过,若有,则升级为悲观读,重读,若无,则无锁偷鸡成功if (!stampedLock.validate(stamp)){System.out.println("中途被写线程修改过!!!");stamp = stampedLock.readLock();try {System.out.println("已从乐观读升级为悲观读.......");result = number;System.out.println("悲观读重读的结果:" + result);} finally {stampedLock.unlockRead(stamp);}}//最终结果System.out.println("final result: " + result);
}
测试类同上,启动两个线程充当读线程和写线程:
再调,6妙后,写线程介入,发现乐观读偷鸡成功:
6、邮戳锁的缺点
- StampedLock 不支持重入,没有Re开头
- StampedLock 的悲观读锁和写锁都不支持条件变量 (Condition)
- 使用stampedLock一定不要调用中断操作,即不要调用interrupt()方法
7、终章回顾
到此,JUC课程整理完成。2023/12/21 10:54
画个xmind图整体梳理一遍:
顺畅多了,舒服了。之前课完结,最后一篇笔记整理完直接走人,这次按课程老师说的串一遍,不错的习惯。
相关文章:

【JUC】三十二、邮戳锁StampedLock
文章目录 1、邮戳锁2、锁饥饿问题的解决思路3、邮戳锁的特点4、代码演示:邮戳锁的传统读写用法5、代码演示:邮戳锁之乐观读6、邮戳锁的缺点7、终章回顾 前面提到了从无锁 ⇒ 独占锁 ⇒ 读写锁,但读写锁存在写锁饥饿的情况。 📕【读…...

城市里的“蛋壳运动空间”
近年来,秉承"发展群众体育,服务健康中国”的理念,全国各地持续推进全民健身与全民健康的融合发展。越来越多的口袋公园、户外运动设施出现在城市各个角落,一定程度上提升了全民运动的便利性和幸福感。 但是,遇到…...

Linux宝塔面板本地部署Discuz论坛发布到公网访问【无需公网IP】
文章目录 前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 前言 Crossday Discuz! Board(以下简称 Discuz!)是一套通用的社区论坛软件系统,用户可以在不需要任何编程的基础上&a…...
Android Canvas状态save与restore,Kotlin
Android Canvas状态save与restore,Kotlin private fun f1() {val bitmap BitmapFactory.decodeResource(resources, R.mipmap.pic).copy(Bitmap.Config.ARGB_8888, true)val canvas Canvas(bitmap)val paint Paint(Paint.ANTI_ALIAS_FLAG)paint.color Color.RED…...

python爬取网页图片并下载
python爬取网页图片并下载之GET类型 准备工作 【1】首先需要准备好pycharm,并且保证环境能够正常运行 【2】安装request模块 pip install requestsimport request导入request内置模块 【3】安装lxml模块 pip install lxmlfrom lxml import etree导入lxml.etre…...

亚马逊prime会员日活动是免费的吗?prime day怎么选产品促销?——站斧浏览器
亚马逊prime会员日活动是免费的吗? 实际上,亚马逊prime会员日活动并不是免费的。亚马逊prime会员日是亚马逊推出的一项会员特权服务,只有成为亚马逊prime会员的消费者才能享受该项服务。而成为亚马逊prime会员需要支付一定的费用,…...

二叉树题目:输出二叉树
文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:输出二叉树 出处:655. 输出二叉树 难度 6 级 题目描述 要求 给定二叉树的根结点 root \textt…...
apache poi_5.2.5 实现对表格单元格的自定义变量名进行图片替换
apache poi_5.2.5 实现对表格单元格的自定义变量名进行图片替换 实现思路 1.首先定位到自定义变量名 2.然后先清除自定义变量名,可利用setText(null,0)来清除 3.在自定义变量名的位置添加图片,使用下面的代码 4.对于图片布局有要求的,利用C…...

Kafka--Kafka日志索引详解以及生产常见问题分析与总结
一、Kafka的Log日志梳理 这一部分数据主要包含当前Broker节点的消息数据(在Kafka中称为Log日志)。这是一部分无状态的数据,也就是说每个Kafka的Broker节点都是以相同的逻辑运行。这种无状态的服务设计让Kafka集群能够比较容易的进行水平扩展。比如你需要用一个新…...

Vue3-23-组件-依赖注入的使用详解
什么是依赖注入 个人的理解 : 依赖注入,是在 一颗 组件树中,由 【前代组件】 给 【后代组件】 提供 属性值的 一种方式 ;这种方式 突破了 【父子组件】之间通过 props 的方式传值的限制,只要是 【前代组件】提供的 依…...

css 美化滚动条
当div内容溢出容器定义的高度时,滚动条显示,并美化默认的滚动条样式 div 容器 <divclass"content">内容 </div>css 样式 /* 问话区域 滚动条 */ .content {overflow: auto;height: 662px;padding: 25px;scrollbar-width: thin; /* 设置滚动条宽度 */bo…...
Tomcat介绍及使用:构建强大的Java Web应用服务器
引言: 在现代软件开发中,Web应用已经成为了不可或缺的一部分。而为了构建高效、稳定的Web应用服务器,选择合适的工具和技术至关重要。Tomcat作为一款开源的Java Web应用服务器,凭借其丰富的功能和灵活的配置,成为了开发…...

怎么定义一套完成标准的JAVA枚举类型
一、背景 在java代码中,接口返回有各种各样的状态,比如400 401 200 500 403等常见的http状态码,也有我们自定义的很多业务状态码。如果系统比较复杂,制定一套完整的标准的状态码是非常有必要的,这样比较方面BUG排查。…...

Apache Seatunnel本地源码构建编译运行调试
Apache Seatunnel本地源码构建编译运行调试 文章目录 1. 环境准备1.1 Java环境1.2 Maven1.3 IDEA1.4 Docker环境1.5 Mysql8.0.281.6 其它环境准备 2. 源码包下载3. idea项目配置3.1 项目导入3.2 maven配置3.3 项目JDK配置3.4 项目启动参数配置3.4.1 seatunnel项目启动参数配置3…...

构建高效持久层:深度解析 MyBatis-Plus(02)
目录 引言1. 逻辑删除1.1 概述1.2 逻辑删除的优势1.3.为什么使用逻辑删除1.4 综合案例 2. 乐观锁和悲观锁2.1.什么是乐观锁和悲观锁2.2.乐观锁和悲观锁的区别2.3.综合案例 3. 分页插件总结 引言 在现代软件开发中,数据库操作是不可或缺的一环。为了提高系统的性能、…...

Gitlab仓库推送到Gitee仓库的一种思路
文章目录 Gitlab仓库推送到Gitee仓库的一种思路1、创建Gitee的ssh公钥(默认已有Gitlab的ssh公钥)2、添加Gitlab远程仓库地址3、添加Gitee远程仓库地址4、拉取Gitlab远程仓库指定分支到本地仓库指定分支(以test分支为例)5、推送本地…...

快速能访问服务器的文件
1、背景 访问ubuntu上的文件 2、方法 python3 -m http.server 8081 --directory /home/ NAS 共享访问协议 — NFS、SMB、FTP、WebDAV 各有何优势?http://1 Ubuntu 搭建文件服务器(Nginx)...

Diary26-Vue综合案例1-书籍购物车
Vue综合案例1-书籍购物车 案例要求: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…...

【EasyExcel实践】万能导出,一个接口导出多张表以及任意字段(可指定字段顺序)-简化升级版
文章目录 前言正文一、项目简介二、核心代码2.1 pom.xml 依赖配置2.2 ExcelHeadMapFactory2.3 ExcelDataLinkedHashMap2.4 自定义注解 ExcelExportBean2.5 自定义注解 ExcelColumnTitle2.6 建造器接口 Builder2.7 表格工具类 ExcelUtils2.8 GsonUtil2.9 模版类 ExportDynamicCo…...

解决 Hive 外部表分隔符问题的实用指南
简介: 在使用 Hive 外部表时,分隔符设置不当可能导致数据导入和查询过程中的问题。本文将详细介绍如何解决在 Hive 外部表中正确设置分隔符的步骤。 问题描述: 在使用Hive外部表时,可能会遇到分隔符问题。这主要是因为Hive在读…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...