Java多线程-StampedLock(原子读写锁)

StampedLock 是读写锁的实现,对比 ReentrantReadWriteLock 主要不同是该锁不允许重入,多了乐观读的功能,使用上会更加复杂一些,但是具有更好的性能表现。StampedLock 的状态由版本和读写锁持有计数组成。 获取锁方法返回一个邮戳,表示和控制与锁状态相关的访问; 这些方法的“尝试”版本可能会返回特殊值 0 来表示获取锁失败。 锁释放和转换方法需要邮戳作为参数,如果它们与锁的状态不匹配则失败。

但是也是由于 StampedLock 大量使用自旋的原因(ReentrantReadWriteLock 也使用了自旋,但是没有 StampedLock 频繁),CPU 的消耗理论上也比 ReentrantReadWriteLock 高。

StampedLock 非常适合写锁中的操作非常快的业务场景。因为读锁如果因为写锁而获取锁失败,读锁会做重试获取和有限次的自旋的方式,比较晚进入到等待队列中。如果在自旋过程中,写锁能释放,那么获取读锁的线程就能避免被操作系统阻塞和唤醒等耗资源操作,增加读锁的响应效率。

三种模式

悲观读锁

与 ReentrantReadWriteLock 的读锁类似,多个线程可以同时获取悲观读锁。这是一个共享锁,允许多个线程同时读取共享资源。

乐观读锁

相当于直接操作数据,不加任何锁。在操作数据前并没有通过 CAS 设置锁的状态,仅仅通过位运算测试。如果当前没有线程持有写锁,则简单地返回一个非 0 的 stamp 版本信息。返回 0 则说明有线程持有写锁。获取该 stamp 后在具体操作数据前还需要调用 validate 方法验证该 stamp 是否己经不可用。

写锁

与 ReentrantReadWriteLock的写锁类似,写锁和悲观读锁是互斥的。虽然写锁与乐观读锁不会互斥,但是在数据被更新之后,之前通过乐观读锁获得的数据已经变成了脏数据,需要自己处理这个。

StampedLock 的读写锁都是不可重入锁,所以在获取锁后释放锁前不应该再调用会获取锁的操作,以避免造成调用线程被阻塞。

在实际应用中,StampedLock 可以用于那些读操作远多于写操作的场景,例如缓存系统、数据报表生成等。在这些场景中,StampedLock 可以显著提高并发性能,同时保证数据的一致性和安全性。

最重要的一点: 在使用时需要特别注意:如果某个线程阻塞在StampedLock的readLock()或者writeLock()方法上时,此
时调用阻塞线程的interrupt()方法中断线程,会导致CPU飙升到100%。

所以尽量在写操作是非常快的场景下使用, 这样读的时候乐观锁释放的非常快,几乎达到无锁模式。

所有接口方法

image.png
image.png
image.png

经典案例

import java.util.concurrent.locks.StampedLock;public class StampedLockExample {private int inventory = 100; // 初始库存为100private final StampedLock lock = new StampedLock();// 扣减库存操作public void decreaseInventory(int quantity) {long stamp = lock.writeLock(); // 获取写锁try {if (inventory >= quantity) {inventory -= quantity; // 扣减库存System.out.println("成功减少库存 " + quantity + ", 当前的库存量: " + inventory);} else {System.out.println("未能减少库存,库存不足");}} finally {lock.unlockWrite(stamp); // 释放写锁}}// 获取当前库存public int getInventory() {long stamp = lock.tryOptimisticRead(); // 乐观读锁int currentInventory = inventory;if (!lock.validate(stamp)) { // 检查乐观读锁是否有效stamp = lock.readLock(); // 乐观读锁无效,转为悲观读锁try {currentInventory = inventory; // 获取当前库存} finally {lock.unlockRead(stamp); // 释放读锁}}return currentInventory; // 返回当前库存}public static void main(String[] args) {StampedLockExample manager = new StampedLockExample();// 多个线程同时扣减库存Thread t1 = new Thread(() -> {manager.decreaseInventory(20); // 线程1扣减库存System.out.println(manager.getInventory());});Thread t2 = new Thread(() -> {manager.decreaseInventory(50); // 线程2扣减库存System.out.println(manager.getInventory());});t1.start();t2.start();}
}

官网案例

 
public class Point {private double x, y;private final StampedLock sl = new StampedLock();public void move(double deltaX, double deltaY) {使用写锁-独占操作,并返回一个邮票long stamp = sl.writeLock();try {x += deltaX;y += deltaY;} finally {使用邮票来释放写锁sl.unlockWrite(stamp);      }}// 使用乐观读锁访问共享资源// 注意:乐观读锁在保证数据一致性上需要拷贝一份要操作的变量到方法栈,并且在操作数据时候可能其 // 他写线程已经修改了数据,而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是 // 最新的数据,但是一致性还是得到保障的。public double distanceFromOrigin() {使用乐观读锁-并返回一个邮票,乐观读不会阻塞写入操作,从而解决了写操作线程饥饿问题。long stamp = sl.tryOptimisticRead();    拷贝共享资源到本地方法栈中double currentX = x, currentY = y;      if (!sl.validate(stamp)) {              如果验证乐观读锁的邮票失败,说明有写锁被占用,可能造成数据不一致,所以要切换到普通读锁模式。stamp = sl.readLock();             try {currentX = x;currentY = y;} finally {sl.unlockRead(stamp);}}// 如果验证乐观读锁的邮票成功,说明在此期间没有写操作进行数据修改,那就直接使用共享数据。return Math.sqrt(currentX * currentX + currentY * currentY);}// 锁升级:读锁--> 写锁public void moveIfAtOrigin(double newX, double newY) { // upgrade// Could instead start with optimistic, not read modelong stamp = sl.readLock();try {while (x == 0.0 && y == 0.0) {读锁转换为写锁long ws = sl.tryConvertToWriteLock(stamp); if (ws != 0L) {如果升级到写锁成功,就直接进行写操作。stamp = ws;x = newX;y = newY;break;} else {//如果升级到写锁失败,那就释放读锁,且重新申请写锁。sl.unlockRead(stamp);stamp = sl.writeLock();}}} finally {//释放持有的锁。sl.unlock(stamp);}}}

StampedLock和ReentrantReadWriteLock之间的区别

  1. 锁的类型与特性
    • StampedLock:提供了乐观读、悲观读和写锁三种模式。乐观读模式允许在写锁未被持有时进行无锁读取,通过验证戳记(stamp)来确保数据的一致性。这种模式减少了锁的竞争,提高了吞吐量。
    • ReentrantReadWriteLock:允许多个读线程同时访问,但写线程在访问时必须独占。它支持锁的重入,即同一线程可以多次获取同一把锁。
  2. 性能
    • StampedLock:通常比ReentrantReadWriteLock具有更高的性能,特别是在读多写少的场景下。由于乐观读的存在,它能够在无竞争的情况下避免不必要的锁开销。
    • ReentrantReadWriteLock:在读操作远多于写操作的场景中表现良好,但写锁的饥饿问题和锁降级操作可能影响其性能。
  3. 实现机制
    • StampedLock:并非基于AQS(AbstractQueuedSynchronizer)实现,而是使用了自己的同步等待队列和状态设计。其状态为一个long型变量,与ReentrantReadWriteLock的设计不同。
    • ReentrantReadWriteLock:基于AQS实现,通过内部维护的读写锁来实现多线程间的同步。
  4. 使用场景
    • StampedLock:更适合于读多写少且对性能要求较高的场景,尤其是当数据争用不严重时。它能够有效减少锁的竞争,提高系统的吞吐量。
    • ReentrantReadWriteLock:适用于需要重入锁或需要在写操作后降级为读锁的场景。它提供了更严格的访问控制,但可能在某些情况下牺牲了一定的性能。
  5. 锁的获取与释放
    • StampedLock:在获取锁时会返回一个戳记(stamp),用于后续的锁释放或转换。这个戳记代表了锁的状态,有助于在释放锁时验证数据的一致性。
    • ReentrantReadWriteLock:没有戳记的概念,锁的获取和释放相对简单直接。

综上所述,StampedLock和ReentrantReadWriteLock各有其特点和适用场景。在选择使用哪种锁时,应根据具体的应用需求和性能要求来做出决策。

点赞 -收藏 -关注
有问题在评论区或者私信我-收到会在第一时间回复

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/349473.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

报名进行中 | ISCSLP2024 对话语音克隆挑战赛(CoVoC)

晴数智慧(Magic Data)联合西北工业大学音频语音与语言处理研究组(ASLPNPU)、新加坡资讯通讯研究院(I2R)、深圳大数据研究院(SRIBD)、香港中文大学(深圳)等多家单位在2024年中文口语语言处理国际会议(ISCSLP2024)上推出对话语音克隆挑战赛(Conversational Voice Clone Challenge…

【leetcode--同构字符串】

要求:判断两个字符串的形式是不是一致,即是不是AABC或者ABBBCC这种。 trick:使用set()结合zip()。 set()用法:用于创建一个不包含重复元素的集合 zip&#…

FPGA+金融|硬件行情加速系统 打造极速交易场景

会议时间:2024年06月20日(周四)下午13:50 FPGA金融|硬件行情加速系统 打造极速交易场景_中科亿海微_芯有灵犀 智创未来

安装前端依赖node-sass报错

文章目录 问题1:node-sass报错问题2:node-gyp报错问题3:node-sass再次报错问题4:node-sass三次报错 问题1:node-sass报错 问题描述:经常会碰到一个新的项目安装依赖时,会报node-sass版本的问题…

《C++ Primer》导学系列:第 1 章 - 开始

1.1 编写一个简单的C程序 概述 本小节介绍了如何编写和运行一个简单的C程序,帮助初学者了解C程序的基本结构和编译运行过程。 编写第一个C程序 我们从一个简单的C程序开始,它的功能是在控制台输出 "Hello, World!"。这是学习任何编程语言的…

File及典型案例

File File对象表示一个路径,可以是文件的路径,也可以是文件夹的路径 这个路径可以是存在的,也允许不存在 常见的构造方法 图来自黑马程序员网课 package com.lazyGirl.filedemo;import java.io.File;public class Demo1 {public static vo…

easyexcel的简单使用(execl模板导出)

模板支持功能点 支持列表支持自定义头名称支持自定义fileName支持汇总 模板示例 操作 pom引入 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>${easyexcel.version}</version></dep…

github 本地仓库上传及报错处理

一.本地文件上传 这里为上传部分&#xff0c;关于gitbash安装配置&#xff0c;读者可自行搜索&#xff0c;由于已经安装完成&#xff0c;未进行截图保存&#xff0c;这里便不做赘述。 1.登录git账号并创建一个仓库 点击仓库打开后会看到这个仓库的网址链接&#xff08;这个链…

防止暴力破解,教你如何在登录失败后实施10分钟账户锁定策略!

最近&#xff0c;在服务器上发现了异常的登录尝试。尽管您的团队已经采取了强密码策略和其他安全措施来加固服务器&#xff0c;但恶意程序仍然通过暴力破解的方式试图多次尝试猜测正确的凭据以获取访问权限。为了增强系统的安全性&#xff0c;特别是防止此类暴力破解攻击&#…

项目实战--文档搜索引擎

在我们的学习过程中&#xff0c;会阅读很多的文档&#xff0c;例如jdk的API文档&#xff0c;但是在这样的大型文档中&#xff0c;如果没有搜索功能&#xff0c;我们是很难找到我们想查阅的内容的&#xff0c;于是我们可以实现一个搜索引擎来帮助我们阅读文档。 1. 实现思路 1…

ComfyUI-全民舞王-MusePose

musepose-demo 项目介绍 https://github.com/TMElyralab/MusePose发布的模型能够根据给定的姿势序列&#xff0c;生成参考图中人物的舞蹈视频&#xff0c;结果质量超越了同一主题中几乎所有当前开源的模型。发布pose align算法&#xff0c;以便用户可以将任意舞蹈视频与任意参…

第二届人工智能、系统与网络安全国际学术会议 (AISNS 2024)

第二届人工智能、系统与网络安全国际学术会议 (AISNS 2024&#xff09; 2024 2nd International Conference on Artificial Intelligence, Systems and Network Security 一、重要信息 大会官网&#xff1a;www.aisns.org &#xff08;点击参会/投稿/了解会议详情&#xff09…

基于GTX 8B10B编码的自定义PHY上板测试(高速收发器十四)

前文整理了GTX IP&#xff0c;完成了自定义PHY协议的收发模块设计&#xff0c;本文将通过光纤回环&#xff0c;对这些模块上板测试&#xff0c;首先需要编写一个用于生成测试数据的用户模块。 1、测试数据生成模块 本模块用于生成自定义PHY协议的测试数据&#xff0c;通过axi_…

什么是网站页面组件,有什么用处

1.组件是页面的组成部分 网站的每个页面都是由每个小组件拼接而成的&#xff0c;当然了&#xff0c;有一些网站并不是用小组件拼接的&#xff0c;但是像这些网站它是固定住标签&#xff0c;然后用js直接调用数据显示&#xff0c;这个就是定制化的体现。像那些用小组件组…

Elastic 索引结构-倒排索引

前言 Elastic 在数据库分类中一般被分为全文检索的数据库&#xff0c;那为什么这么区分呢&#xff1f;主要是因为其独特的索引结构 即倒排索引。 倒排索引 倒排索引先将文档中包含的关键字全部提取出来&#xff0c;然后再将关键字与文档的对应关系保存起来&#xff0c;最后再…

多模态大模型:识别和处理图片与视频的技术详解

多模态大模型&#xff1a;识别和处理图片与视频的技术详解 多模态大模型&#xff1a;识别和处理图片与视频的技术详解1. 什么是多模态大模型&#xff1f;2. 多模态大模型的基本架构3. 识别和处理图片3.1 图像特征提取3.2 图像分类与识别3.3 图像生成与增强 4. 识别和处理视频4.…

WebSocket 快速入门 与 应用

WebSocket 是一种在 Web 应用程序中实现实时、双向通信的技术。它允许客户端和服务器之间建立持久性的连接&#xff0c;以便可以在两者之间双向传输数据。 以下是 WebSocket 的一些关键特点和工作原理&#xff1a; 0.特点&#xff1a; 双向通信&#xff1a;WebSocket 允许服务…

什么是端口转发?路由器如何正确的设置端口转发和范围转发?(外网访问必备设置)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 端口转发 📒🚀 端口转发的应用场景💡 路由器如何设置端口转发(示例)💡 端口范围转发(示例)🎯 范围转发的应用场景🛠️ 设置范围转发📝 范围转发实操示例🎈 注意事项 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 …

遗传算法求解车间调度问题(附python代码)

背景介绍 车间调度问题&#xff08;Job Shop Scheduling Problem, JSSP&#xff09;是一类经典的组合优化问题&#xff0c;它在制造业和生产管理中有着广泛的应用。JSSP 的目标是对车间中的一系列作业进行排程&#xff0c;以使得作业在不同机器上的加工顺序是最优的&#xff0…

什么是场外期权?场外期权有几种做法?

今天带你了解什么是场外期权&#xff1f;场外期权有几种做法&#xff1f;期权分为场内期权&#xff0c;场外期权。场内期权我们都知道&#xff0c;是在期货盘里购买的期权&#xff0c;但场外期权呢&#xff1f; 什么是场外期权&#xff1f; 场外期权是一种在交易所之外进行交易…