Java 标准库 API 系列之 StampedLock

本贴最后更新于 475 天前,其中的信息可能已经东海扬尘

在学习蚂蚁金服实现的 Raft 算法实现 sofa-jraft 项目中第一次接触到这个锁。jraft 使用 StampedLock 来实现对于 raft group 的路由信息的线程安全。

StampedLock 是 Java 8 中引入的新的锁机制,它支持三种模式的访问控制,分别是读模式、写模式和乐观读模式,它可以在高并发的情况下提供更好的性能和吞吐量。

在 StampedLock 中,每个线程都需要获取一个戳(stamp)来进行读或写操作,这个戳是一个 64 位的数字,其中高 16 位代表一个版本号,低 48 位代表一个序号。线程可以使用 tryOptimisticRead()方法来获取一个乐观读锁,这种方式不会阻塞线程,只是获取一个戳,如果获取到的戳发生了变化,就意味着有其他线程已经修改了共享资源,这时候需要重新获取锁。

StampedLock 还支持读锁和写锁。读锁是共享锁,多个线程可以同时持有读锁,并发地读取共享资源,而写锁是独占锁,只有一个线程可以持有写锁,其他线程需要等待写锁释放才能继续执行。

StampedLock 的优势在于,对于读多写少的场景,使用乐观读锁可以避免大量线程阻塞,提高系统的吞吐量;对于写操作比较频繁的场景,使用 StampedLock 可以减少锁的竞争,提高系统的性能。

API

StampedLock 有三种锁的获取方式:

  1. 读锁(readLock()):多个线程可以同时持有读锁,只要没有线程持有写锁。读锁是乐观的且不可重入的。通过调用 tryOptimisticRead()方法获取读锁,如果锁可用,会返回一个非零的标记(stamp)。
  2. 写锁(writeLock()):只有一个线程可以持有写锁,它排除所有的读锁和写锁。写锁是悲观的且可重入的。通过调用 writeLock()方法获取写锁。
  3. 乐观读锁(Optimistic Read Lock):乐观读锁是一种特殊的读锁,它允许其他线程在获取读锁之前修改共享数据,但在验证阶段检测到数据被修改时,需要重新获取锁。乐观读锁的获取方式是调用 tryOptimisticRead()方法,如果锁可用,会返回一个非零的标记(stamp)。

下面是一个简单的 StampedLock 的使用示例:

import java.util.concurrent.locks.StampedLock;

public class Point {
    private double x;
    private double y;
    private final StampedLock lock = new StampedLock();

    public void setPoint(double x, double y) {
        long stamp = lock.writeLock();
        try {
            this.x = x;
            this.y = y;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();
        double currentX = x;
        double currentY = y;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

在上述示例中,setPoint() 方法使用写锁来更新坐标。写锁是 排他锁,当一个线程获取写锁时,其他线程无法获取读锁或写锁。

distanceFromOrigin() 方法使用乐观读锁尝试获取点的当前坐标,并计算点到原点的距离。如果乐观读锁验证失败(即期间有写操作),则通过获取悲观读锁来重新读取坐标。

这个例子展示了 StampedLock 的乐观读锁和悲观写锁的使用方式,通过合理地选择锁的模式,可以提供更高的并发性能,并且允许读操作和写操作同时进行,而不会相互阻塞。

校验是否有写操作 是如何判断的

StampedLock 的写锁有一个版本号,读锁没有版本号。因此,StampedLock 可以通过判断当前读锁的版本号是否等于写锁的版本号来检查是否存在写锁。如果版本号相同,说明当前存在写锁,反之,则没有写锁。需要注意的是,在使用 StampedLock 时,如果读锁和写锁是在不同的线程中获取的,那么在判断是否存在写锁时可能存在一定的延迟,因为写锁的版本号可能已经变化,但是读锁的版本号还没有更新。因此,在这种情况下,可能会出现一定的误判。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3170 引用 • 8209 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 安装

    你若安好,便是晴天。

    131 引用 • 1184 回帖
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 563 关注
  • Webswing

    Webswing 是一个能将任何 Swing 应用通过纯 HTML5 运行在浏览器中的 Web 服务器,详细介绍请看 将 Java Swing 应用变成 Web 应用

    1 引用 • 15 回帖 • 623 关注
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    176 引用 • 814 回帖
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 4 关注
  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 626 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 518 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 450 关注
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用 • 1 关注
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 383 回帖
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    336 引用 • 641 回帖 • 1 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    5 引用 • 18 回帖 • 157 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 1 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 708 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 1 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用 • 5 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 388 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 115 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 268 回帖 • 84 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 51 关注
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    88 引用 • 1234 回帖 • 429 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    124 引用 • 580 回帖 • 1 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖 • 1 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 1 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 731 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖