ReentrantLock实现原理-条件锁

news2024/11/20 10:40:14

在前两篇文章中,我们了解了ReentrantLock内部公平锁和非公平锁的实现原理,可以知道其底层基于AQS,使用双向链表实现,同时在线程间通信方式(2)中我们了解到ReentrantLock也是支持条件锁的,接下来我们来看下,其内部条件锁的实现原理。

条件锁的使用

public static void main(String[] args) {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+" enter lock first");
            System.out.println(Thread.currentThread().getName()+" await start");
            try {
                condition.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" await end");
            lock.unlock();
        }
    });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+" enter lock first");
            System.out.println(Thread.currentThread().getName()+" start sleep");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" end sleep");
            System.out.println(Thread.currentThread().getName()+" signalAll condition");
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"signal end");
            lock.unlock();
        }
    });
}

如上代码所示,一般情况下我们通过

Condition condition = lock.newCondition();

创建条件对象,使用condition.await();表示当前线程需要等待条件才能继续执行,当线程执行到此处时,会进入等待队列等待,直到有另一个线程通过condition.signalAll();condition.signal();唤醒,此时表明当前线程执行条件已具备,此时当前线程继续执行,上述代码中,当前线程会转入AQS的同步等待队列中,去等待抢占lock锁,其运行结果如下图所示:

1-4-13.1

条件锁一般适用于线程需要具备一定条件后才能正确执行的情况。

ReentrantLock.newCondition()

上文看到Condition的创建和基本用法,接下来我们来看下Condition的实现原理,跟踪ReentrantLock的执行代码如下所示:

// ReentrantLock.java 
public Condition newCondition() {
    return sync.newCondition();
}

// ReentrantLock内部类Sync中
final ConditionObject newCondition() {
    return new ConditionObject();
}

可以看到newCondition最终返回了一个ConditionObject类的对象,ConditionObject类代码如下所示:

// AQS中声明的ConditionObject
public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;
    private transient Node firstWaiter;
    private transient Node lastWaiter;

    public ConditionObject() { }


    private Node addConditionWaiter() {

    }

    private void doSignal(Node first) {
      .....
    }

    private void doSignalAll(Node first) {
      .....
    }

    private void unlinkCancelledWaiters() {
      .....
    }

相信大家已经看出来了,很熟悉的Node链表有没有?其中firstWaiter指向链表首位,lastWaiter指向链表尾,在该链表内维护一个Node的双向链表,结合AQS中实现,我们可以猜测出,在condition.await的时候会以当前线程创建Node节点,随后以插入条件队列,随后当执行condition.signal/condition.signalAll时,唤醒在链表上的这些节点,具体实现是不是这样呢?我们继续看

Condition.await

ConditionObject实现的await方法如下所示:

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 以当前线程创建Node对象,并添加值队尾
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 通过LockSupport阻塞线程
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

Condition.signal

ConditionObject中的signal函数实现如下所示:

public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        // 对队首节点唤醒
        doSignal(first);
}
private void doSignal(Node first) {
    do {
        // 重置firstWaiter并不断尝试唤醒首节点
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
    // 尝试更新节点的waitStatus
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
    // 当前线程可以正常执行了,将该节点移入同步等待队列中,尝试获取锁
    Node p = enq(node);
    int ws = p.waitStatus;
    // 如果可以获取锁,则立即唤醒执行
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

Condition.signalAll的逻辑与signal基本一致,区别在于是将在该条件上等待的所有节点均移入同步等待队列中。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ISO21434 网络安全支持终止与退役(十一)

目录 一、概述 二、目标 三、网络安全支持终止 3.1 输入 3.2 要求和建议 3.3 输出 四、退役 4.1 输入 4.1.1 先决条件 4.1.2 进一步支持信息 4.2 要求和建议 4.3 输出 一、概述 退役不同于结束网络安全支持。一个组织可以结束对一个项目或组件的网络安全支持&#…

【Linux】常用命令总结

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员,2024届电子信息研究生 目录 1.登录操作 2.操作系统 a.os是什么? b.好的操作系统的衡量指标?为什么要有操作系统? 3.Linux下的基本指令 a.ls指令 b.pwd指令 c.cd指令 d…

SB领导的特征

越是SB领导,往往越有这9大特征,难怪员工不服气。 孔子曾说过:“视其所以,观其所由、察其所安、人焉善哉?” 领导也是如此,想要判断领导是不是SB,关键就看他的所作所为。 越是“SB”的领导&#…

在CRM系统中如何获取联系人的信息?

CRM客户管理系统可以有效应对企业对联系人管理的需求,帮助销售人员随时随地查阅、记录、修改联系人,为业务开展做好铺垫。CRM中联系人是什么?如何获取联系人信息? 1.CRM中联系人是什么? CRM系统联系人指的是沟通对象…

薅羊毛!阿里云免费GPU云主机畅玩AI绘画,免费领取阿里云v100显卡搭建AI绘画利器Stable Diffusion

❝ AI绘画利器Stable Diffusion,因为对电脑显卡,磁盘,内存等都有一些限制,导致大部分人望而止步,所以小编今天教大家免费领取阿里云的免费显卡服务器,免费试用3个月,以方便各位快速学习与使用。 ❞ 领取免费…

MapReduce编程

Hadoop的MapReduce计算框架 概述 MapReduce计算框架是一种计算框架,用于计算处理大规模的数据集,他将数据分成小块,然后在集群中的多个节点上并行处理这些块MapReduce框架是由两个组件组成:Map和Reduce Map任务将输入数据分解成…

团队管理之性能实施团队日志12

这几天算是多事之秋。本来就有几个严重的问题天天在折腾。 还是出现了各种差错。 其实对于做项目来说,就是这样,总会有紧要的事情突然冒出来。 我倒是习惯了这种状态。 只是时间不等人。 这两天在写各系统的最终报告。结果写到某个系统的时候发现&#…

零基础学Python,最受推荐的10本Python书籍

目录 适合初学者的最佳Python书籍1、《Python编程:从入门到实践》2、《Head-First Python (2nd edition)》3、《“笨方法”学Python》4、《Python程序设计(第3版)》 最适合初学者的免费Python书籍5、《像计算机科学家一样思考Python (第2版)》…

学习【菜鸟教程】【C++ 继承】(未完)

链接 1. 教程原文 面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。 当创建一个类时,您不需要重新编写新的…

0基础学习VR全景平台篇第41篇:编辑器底部菜单-素材管理

大家好,欢迎观看蛙色VR官方系列——后台使用课程! 本期为大家带来蛙色VR平台,底部菜单—素材管理功能操作。 功能位置示意 一、本功能将用在哪里? 素材管理,主要是对场景列表相关的基础操作做一个详细的讲解&#xf…

自研API 网关 - 媲美美团这套Shepherd网关架构!

作者:小傅哥 博客:https://bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!😄 我说:“很多互联网大厂,很少基于 SpringMVC 模块对外提供 WEB 服务的 HTTP 接口!” 一下炸窝…

【瑞萨RA MCU创意氛围赛】基于FSP库开发 --- Qt遥控小车

文章目录 引言一、产品功能介绍二、硬件清单三、软件设计1. RASC2. Keil2. Qt 四、视频展示 引言 很高兴能够加入“野火”这个大家庭中,且能有机会参加第一期的【瑞萨RA MCU创意氛围赛】,在此之前,我竟都没能了解瑞萨这个产品,是…

如何计算一个实例占用多少内存?

我们都知道CPU和内存是程序最为重要的两类指标,那么有多少人真正想过这个问题:一个类型(值类型或者引用类型)的实例在内存中究竟占多少字节?我们很多人都回答不上来。其实C#提供了一些用于计算大小的操作符和API&#…

【026】C++的内联函数、函数重载、函数的默认参数与占位参数

C的内联函数、函数重载、函数的默认参数与占位参数 引言一、内联函数1.1、声明内联函数1.2、宏函数和内联函数的区别1.3、内联函数的注意事项 二、函数重载2.1、函数重载的概述2.2、函数重载的条件2.3、函数重载的底层实现原理 三、函数的默认参数四、占位参数五、extern "…

STM32单片机TFT显示AD9833 DDS信号发生器语音播报正弦波方波三角波

实践制作DIY- GC0146---TFT显示AD9833 DDS信号发生器 基于STM32单片机设计---TFT显示AD9833 DDS信号发生器 二、功能介绍: 硬件组成:STM32F103C系列最小系统板 1.8寸TFT彩屏AD9833信号模块4*4矩阵键盘DY-SV17F语音播报模块 1.通过4*4键盘来设定频率值和…

ExpertLLaMA:超越Vicuna,通过角色扮演增强指令,显著提升回答质量

本文向大家介绍我们刚刚开源的对话模型及相应的训练数据。 首先是 git Repo 和 paper 链接,欢迎大家给我们⭐star⭐ 论文标题: ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 论文链接: https://arxiv.or…

.Net7矢量化的性能优化

前言 矢量化是性能优化的重要技术,也是寄托硬件层面的优化技术。本篇来看下。文章来源:微软官方博客 概括 一:矢量化支持的问题: 矢量化的System.Runtime.Intrinsics.X86.Sse2.MoveMask 函数和矢量化的Vector128.Create().Extract…

get 、post请求 后台@RequestParam、@RequestBody 接收的方法集合

post、get请求 参数:数组、JSON对象、JSON字符串、地址栏 RequestParam、RequestBody 后端接收 //定义json对象,同时包含数组 var _queryData { jflb:"婚姻家庭纠纷",zlay:"xxxxx",ysCode:["0123","4567"]}…

机柜PDU与普通插座的区别,以及如何选择品牌专业PDU产品详解

PDU(Power Distribution Unit),是将来自UPS的输出电流分配到各个IT设备的末端配电设备,是连接供电等基础设施与IT系统、关联机房内所有设备正常运转的关键设备。作为机房用电安全的重要保障,PDU设备的稳定与安全直接关…

FPGA驱动FT601实现USB3.0相机HDMI视频采集 提供工程源码和QT上位机源码

目录 1、前言2、FT601芯片解读和时序分析FT601功能和硬件电路FT601读时序解读FT601写时序解读 3、我这儿的 FT601 USB3.0通信方案4、详细设计方案5、vivado工程详解6、上板调试验证7、福利:工程代码的获取 1、前言 目前USB3.0的实现方案很多,但就简单好…