Java ~ Lock【总结】

ops/2024/10/22 21:40:04/

前言


 相关系列

  • 《Java ~ Lock【目录】》(持续更新)
  • 《Java ~ Lock【源码】》(学习过程/多有漏误/仅作参考/不再更新)
  • 《Java ~ Lock【总结】》(学习总结/最新最准/持续更新)
  • 《Java ~ Lock【问题】》(学习解答/持续更新)

 涉及内容

  • 《Java ~ AQS ~ Condition【总结】》
  • 《Java ~ Lock ~ ReentrantLock【总结】》
  • 《Java ~ JVM ~ synchronized【总结】》

一 概述


 简介

    Lock(锁)接口定义了API层面上锁的概念。谈到锁,我们很容易能联想到关键字synchronized,其是Java在JVM层面上提供的用于在多线程环境下对共享资源进行同步访问的锁实现。因此自然而然地,我们会将锁接口与之进行对比。但实际上如果从功能的角度来说,将两者进行对比是不合适的。因为synchronized关键字是真正能够提供锁功能的实现,而锁接口仅仅是在API层面上对锁进行了定义,并无法提供实际的锁功能。因此两者在维度上是不平等的,如果真的要与synchronized关键字在具体功能上进行对比的话,使用锁接口的经典实现类ReentrantLock(可重入锁)会更合适,因为两者都是基于重入/排它特性的实现。但话虽如此,如果从结构的角度来看,将两者进行对比也并非完全不可行。例如相比synchronized关键字只加不解(其解锁由JVM自动实现,在手动的角度上只有加锁而没有解锁的说法)的结构而言,锁接口将加/解锁都定义为了人为操作。该结构定义使得锁接口实现类的使用变得稍显复杂,因为开发者需要人为保证“加锁后一定解锁,解锁前必须加锁”的行为准则不被破坏,因此锁接口实现类通常都需要搭配try-catch-finally使用。但相对地这也令锁接口实现类在使用上具备了更强的灵活性,使得多个锁域间的交互除了包含外还可以出现如下图般的交叉情况存在。

在这里插入图片描述

    锁接口定义了阻塞不可中断/阻塞/特殊值/超时四种加锁形式。与synchronized关键字只能无限等待到成功加锁不同,锁接口为加锁定义了四种不同的形式以使得实现类能够满足开发中的实际场景所需,其具体名称及表现形式如下所示:

阻塞不可中断 ~ 如果锁允许被持有则持有并返回;否则无限等待至锁允许被持有为止。线程在进入方法/等待持有期间如果已/被中断不会抛出中断异常,但中断状态会被保留。

阻塞 ~ 如果锁允许被持有则持有并返回;否则无限等待至锁允许被持有为止。线程在进入方法/等待持有期间如果已/被中断会抛出中断异常,但中断状态会被清除。

特殊值 ~ 如果锁允许被持有则持有并返回true;否则返回false。

超时 ~ 如果锁允许被持有则持有并返回true;否则有限等待至锁允许被持有为止;超出指定等待时间则返回false。线程在进入方法/等待持有期间如果已/被中断会抛出中断异常,但中断状态会被清除。

    锁接口允许锁关联多个条件。所谓条件,本质是Condition(条件)接口的实现类对象,该接口被设计用于定义条件机制。条件机制的本质是线程管理机制,用于在并发环境下实现对线程的有序协调及精确控制,即令线程有序的等待/唤醒。锁接口引入条件机制目的是为了对访问锁的线程提供更高层面的管理,之所以说是更高等级,是因为锁作为具备令线程等待/唤醒定义的API本身也属于管理机制。一个对条件机制非常大的误解是:由于条件接口在Java JDK中有且仅有AQS类内部定义/实现的ConditionObject(条件对象)一个实现类,并且锁接口被人熟知的经典实现类可重入锁直接复用了AQS类的条件机制实现,因此导致许多开发者产生了所谓条件机制就是AQS类/可重入锁类条件机制实现的认知,并进而将之与功能相似的synchronized关键字wait/notify(等待/通知)机制进行对比,因为其与AQS类/可重入锁类的条件机制实现在功能上相近。这是非常错误的观点,因为条件接口/条件机制仅提供令线程等待/唤醒的基本功能定义,并不含任何逻辑性/目的性。线程具体会出于什么原因/条件/目的,又是以什么方式等待/唤醒这些都由实现类具体实现,因此AQS类/可重入锁类的条件机制只是条件机制的一种实现而无法全权代表条件机制。故而只要愿意/需要,开发者完全可以基于自身设计/需求构造一套与AQS类/可重入锁类完全无关的条件机制实现。

    通过添加各种维度的特性,开发者可以实现远比synchronized关键字功能更加丰富的锁接口实现类。锁接口实现类可添加的维度包含但不限于重入/公平/排它等。锁接口实现类即可以只添加单项维度的特性,也可以多维度实现,例如可重入锁类就是典型同时兼具重入/公平/排它三项维度特性的锁接口实现类。正是因为锁接口实现类的功能完全取决自身添加的维度特性,因此将锁接口在功能上与synchronized关键字进行对比是不合理的。

 与synchronized关键字在“结构”上的对比

  • 锁接口定义了加锁/解锁都必须人为进行,而synchronized关键字虽然需要手动加锁,但解锁却由JVM自动执行;
  • 锁接口定义了阻塞不可中断/阻塞/特殊值/超时四种加锁形式,而synchronized关键字只有阻塞一种;
  • 锁接口定义锁允许显式关联多个条件,而synchronized关键字只能隐式关联锁对象的条件。

二 方法


  • void lock() —— 加锁 —— 令当前线程持有当前锁。该方法是加锁方法“阻塞不可中断”形式的实现,如果当前锁允许被持有则持有;否则无限等待至当前锁允许被持有为止。当前线程在进入方法/等待持有期间如果已/被中断不会抛出中断异常,但中断状态会被保留。
  • void lockInterruptibly() throws InterruptedException —— 加锁(可中断) —— 令当前线程持有当前锁。该方法是加锁方法“阻塞”形式的实现,如果当前锁允许被持有则持有;否则无限等待至当前锁允许被持有为止。当前线程在进入方法/等待持有期间如果已/被中断会抛出中断异常,但中断状态会被清除。
java">Lock lock = new Lock();
lock.lock();
// lock.lockInterruptibly();
try {....
} finally {lock.unlock();
}
  • boolean tryLock() —— 尝试加锁 —— 令当前线程持有当前锁。该方法是加锁方法“特殊值”形式的实现,如果当前锁允许被持有则持有并返回true;否则返回false。
  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException —— 尝试加锁 —— 令当前线程持有当前锁。该方法是加锁方法“特殊值”形式的实现,如果当前锁允许被持有则持有并返回true;否则有限等待至当前锁允许被持有为止,超出指定等待时间则返回false。当前线程在进入方法/等待持有期间如果已/被中断会抛出中断异常,但中断状态会被清除。
java">Lock lock = new Lock();
if (lock.tryLock()) {
// if (lock.tryLock(1, TimeUnit.SECONDS)) {try {....} finally {lock.unlock();}
} else {...
}
  • void unlock() —— 解锁 —— 令当前线程解除对当前锁的持有。如果当前线程未持有当前锁则方法会抛出不受检查异常。
  • Condition newCondition() —— 新条件 —— 创建基于当前锁的新条件以管理访问线程。

http://www.ppmy.cn/ops/33195.html

相关文章

算法学习笔记(最短路——Dijkstra)

D i j k s t r a Dijkstra Dijkstra是最常用,效率最高的最短路径算法,是单源最短路算法。核心是 B F S BFS BFS和贪心。 B F S BFS BFS传送门 D i j k s t r a Dijkstra Dijkstra大概分成以下几个步骤: 从起点出发扩展它的邻点。选择一个最近…

【论文阅读】HAT-Activating More Pixels in Image Super-Resolution Transformer

Activating More Pixels in Image Super-Resolution Transformer 论文地址摘要1 介绍2 相关工作2.1 图像深度网络 SR2.2 Vision Transformer 3 方法3.1 Motivation3.2 Network Architecture总体结构。Residual Hybrid Attention Group (RHAG)。混合注意块(HAB&#…

神经网络中常见的激活函数:理解与实践

神经网络中常见的激活函数:理解与实践 在神经网络中,激活函数是一个非常重要的组成部分,它为神经元引入了非线性特性,使得神经网络可以拟合各种复杂的函数关系。本文将介绍9种常见的激活函数,包括它们的概述、公式以及…

视频改字祝福 豪车装X系统源码uniapp前端小程序源码

视频改字祝福 豪车装X系统源码uniapp前端小程序源码,创意无限!AI视频改字祝福,豪车装X系统源码开源,打造个性化祝 福视频不再难! 想要为你的朋友或家人送上一份特别的祝福,让他们感受到你的真诚与关怀吗&am…

0x002 sqlmap的参数解析

0x002 sqlmap的参数解析 重新看到cmdLineParser()​函数 首先检测是否存在argv​,如果没有则设置为系统默认的命令行参数 再调用checkSystemEncoding()​函数来检测系统的编码设置,确保后续能够正确的处理字符编码 调用os.path.basename(argv[0])​来获取脚本的文件名,再通…

rancher/elemental 构建不可变IOS(一)

一、什么是elemental Elemental 是 Rancher 的一个变种,专注于提供一个更轻量级的 Kubernetes 发行版。它旨在提供简化的部署和管理体验,同时保持 Kubernetes 的灵活性和强大功能。Elemental 通常针对较小的部署场景或资源受限的环境,例如测…

上位机图像处理和嵌入式模块部署(树莓派4b使用lua)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 lua是一个脚本语言,比c语言开发容易,也没有python那么重,整体使用还是非常方便的。一般当成胶水语言进行开发&a…

C语言什么是指向函数的指针?

一、问题 指针可以指向普通数值、数组,还可以指向指针,那么可以指向函数吗?答案是可以, 那么它是什么样的呢? 二、解答 ⼀个函数在编译时被分配⼀个⼊⼜地址,这个地址就称为函数的指针。所以,可…