ReentrantLock实现原理-条件锁

在前两篇文章中,我们了解了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基本一致,区别在于是将在该条件上等待的所有节点均移入同步等待队列中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安卓期末考试知识总结(2)

文章目录 第四章:程序活动单元Activity四大组件Activity生命周期Activity的启动模式standardsingleTopsingleInstancesingleTask IntentIntentFilterActivity之间的数据传递putExtra()方法传递数据Bundle类传递数据Activity之间的数据回传 练习总结 第四章&#xff…

如何管理与改进低质供应商?帮助供应商提升自身质量能力?

管理和改进低质量的供应商可能是一项具有挑战性的任务,但与他们合作以提高他们的能力是可能的。可以采取一些步骤来管理和帮助提高供应商的质量: 确定根本原因:了解供应商产品或服务质量低下的原因。对他们的流程、资源和能力进行全面评估&am…

EFDC建模方法及在地表水环境评价、水源地划分、排污口论证应用

目录 专题一、软件安装 专题二、EFDC模型讲解 专题三、一维河流模拟实操 专题四、建模前处理 专题五、EFDC网格剖分介绍 专题六、EFDC二维湖库水动力模拟/非保守染色剂模拟 专题七、EFDC水质模型参数及原理介绍 专题八、EFDC一、二、三维湖库水质模拟 专题九、基于EFD…

window下安装docker并运行angular项目

window下安装docker并运行angular项目 1、使用场景 本地有一个node项目,node 版本是 v16.13.2,在本地安装的angular 是 15.2.4 但是测试服上面的node 版本是 14.19.3,angular 是1.0.0-beta.28.3 ,会导致angular项目的 ng build …

Submit的使用,程序中调用其他报表

Submit的使用 项目需求:编写一个程序能够将实时的物料库存数据通过发送邮件的形式发送到对应的邮件。现有标准的事务码MB5B来查看对应的库存数据。可以在程序中使用submit将参数传递到MB5B中,然后将获取的数据返回到程序中,然后在执行发送邮件…

线程的生命周期

我是一个线程 第一回 初生牛犊 我是一个线程,我一出生就被编了个号: 0x3704,然后被领到一个昏暗的屋子里,在这里我发现了很多和我一模一样的同伴。 我身边…

计算机视觉 | 语义分割与Segmentation

前 言 「MMSegmentation」 是一个基于 PyTorch 的语义分割开源工具箱。它是 OpenMMLab 项目的一部分。 MMSegmentation v1.x 在 0.x 版本的基础上有了显著的提升,提供了更加灵活和功能丰富的体验。 主要特性 统一的基准平台 我们将各种各样的语义分割算法集成到了…

文件系统原理

文件及硬盘管理是计算机操作系统的重要组成部分,让微软走上成功之路的正是微软最早推出的个人电脑PC操作系统,这个操作系统就叫DOS,即Disk Operating System,硬盘操作系统。我们每天使用电脑都离不开硬盘,硬盘既有大小…

谷歌的passkey是什么?

谷歌的passkey是什么? 谷歌正在研发一种名为“Passkey”的新技术,它将用于用户身份验证。Passkey不同于传统的密码,它采用了硬件加密密钥(如安全密钥或生物识别方式)以及双因素身份验证等技术,可以更好地保…

OpenCV项目开发实战--对图像种的对象进行无缝克隆-附Python、C++的代码实现

文末附基于Python和C++两种方式实现的测试代码下载链接 图 1:无缝克隆示例:一架飞机被克隆到傍晚天空的图片中。 OpenCV 3 中引入的令人兴奋的新功能之一称为无缝克隆。有了这个新功能,您可以从一个图像中复制一个对象,然后将其粘贴到另一个图像中,从而使构图看起来无缝…

做完瑞吉外卖项目的一点笔记和源码

源码在 https://gitee.com/pluto8/take-out 一、软件开发整体介绍 1、软件开发流程 需求分析 :产品原型,需求规格说明书(文档形式)设计:产品文档、UI界面设计、概要设计、详细设计、数据库设计编码:项目…

怎么在eclipse中创建python项目

目录 方法一:借助Eclipse Marketplace安装PyDev插件 方法二:到官网下载手动安装插件 参考文件 方法一:借助Eclipse Marketplace安装PyDev插件 这可以通过Eclipse Marketplace完成。打开Eclipse,然后选择“Help” > “Eclips…

强光LED手电筒方案开发设计

在户外活动中,不管是徒步还是露营,经常需要使用多功能强光手电筒。宇凡微推出的多功能战术强光LED手电筒方案,具有十多年LED灯项目研发经验,方案成熟,支持定制开发。 一、战术强光LED手电筒方案功能介绍 户外使用的LED…

OceanBase 安全审计之身份鉴别

本文主要以 MySQL 和 OceanBase 对比的方式,来介绍 OceanBase(MySQL 模式)安全体系中关于身份鉴别的相关内容,包括身份鉴别机制、用户名组成、密码复杂度、密码过期策略等。 作者:金长龙 爱可生测试工程师,…

Linux环境变量配合权限维持手法

前言: 权限维持的时候有其中有两种,一种是alias别名、第二种是prompt_command,这里我们可以将其添加到环境变量中,每次运行的时候都可以使用,从而达到权限控制的效果,而不是临时执行的效果。 环境变量&am…

(一)初识 Kafka

文章目录 1.发布与订阅消息系统(1)什么是发布与订阅消息系统(2)为什么 Kafka 是数据驱动型应用程序的关键组件 2. Kafka 介绍(1)消息和批次(2)消息模式(3)主题…

【Python】Python进阶系列教程--Python AI 绘画(二十)

文章目录 前言Windows 环境安装Civitai 介绍 前言 往期回顾: Python进阶系列教程-- Python3 正则表达式(一)Python进阶系列教程-- Python3 CGI编程(二)Python进阶系列教程-- Python3 MySQL - mysql-connector 驱动&a…

Java程序员面试1000问,让你开挂的面试宝典,花点耐心看完offer拿到手软

前言: 本文收集整理了各大厂常见面试题N道,你想要的这里都有内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、Linux 等技术栈,希望大家都能找到适…

全量程真空压力综合测量系统的高精度控制解决方案

摘要:针对工作范围在5~1.3Pa,控制精度在0.1%~0.5%读数的全量程真空压力综合测量系统技术要求,本文提出了稳压室真空压力精密控制的技术方案。为保证控制精度,基于动态平衡法,技术方案在高真空、低真空和正压三个区间内…

分布式Profinet IO模块

PROFINET IO模块是一种用于工业自动化控制系统中的设备控制模块。它使用以太网技术,在现场设备和处理器/控制器之间提供快速、精确和可靠的数据交换。PROFINET IO模块通常是面向过程的,可以用于监测和控制工业过程中的各种设备和参数,如传感器…