深入探索ReentrantLock(三):限时锁申请的艺术

 专栏导航

JVM工作原理与实战

RabbitMQ入门指南

从零开始了解大数据

目录

前言

一、ReentrantLock限时锁申请

1.限时锁申请的必要性

2.tryLock(long time, TimeUnit unit) 方法讲解

3.限时锁的优势与注意事项

4.tryLock(long time, TimeUnit unit)案例

总结


前言

Java并发编程中,ReentrantLock作为可重入互斥锁,提供了比synchronized更灵活的控制能力,包括非阻塞锁获取、中断响应及公平锁机制。本文深入探讨ReentrantLock的限时锁,助力开发者构建高效稳定的并发应用。


一、ReentrantLock限时锁申请

在多线程编程中,锁是管理并发访问共享资源的重要机制。ReentrantLock 作为 Java 并发包 java.util.concurrent.locks 下的一个类,提供了比内置同步机制(synchronized 关键字)更灵活、更强大的锁定功能。其中,tryLock(long time, TimeUnit unit) 方法是实现限时锁申请的关键工具,它不仅能有效避免线程无限期等待导致的死锁问题,还能提升系统的响应性和灵活性。

在这里插入图片描述

1.限时锁申请的必要性

在高度复杂且要求精密控制的并发编程环境中,线程可能因为等待访问共享资源而遭遇长时间的阻塞状态。若这种等待过程未设定明确的超时机制,不仅可能引发死锁现象,即多个线程相互等待对方释放资源而永久停滞,还可能因持续占用系统资源而导致整体性能下降乃至资源耗尽的严重后果。

为有效管理这类潜在风险,tryLock(long time, TimeUnit unit) 方法提供了一种灵活且专业的解决方案。此方法允许线程在尝试获取锁的同时,明确指定一个最大等待时间,该时间通过时间长度(long time)及时间单位(TimeUnit unit)共同定义,从而赋予了开发者对线程行为更为精细的控制能力。

若线程在指定的时间范围内成功获取到了锁,它将能够无缝衔接地继续执行其后续关键操作,确保程序的流畅运行。相反,若等待时间耗尽而锁资源仍未被释放,tryLock 方法将立即返回一个失败状态(通常是false),允许线程优雅地放弃当前对锁的竞争,转而执行其他任务或采取其他恢复策略,比如重试机制、回退逻辑或错误处理等。这种方式显著增强了系统的健壮性,有效避免了线程因无限期等待而导致的资源锁定与浪费,是并发编程中处理锁竞争的一种高效且推荐的做法。

2.tryLock(long time, TimeUnit unit) 方法讲解

tryLock(long time, TimeUnit unit) 方法,作为 ReentrantLock 类中的一个标志性成员,展示了在复杂并发环境下对锁管理的高度灵活性和控制能力。此方法通过接收两个关键参数——等待时间长度(long time)和时间单位(TimeUnit unit),允许线程在尝试获取锁时,设置一个明确的时间界限。

该方法的执行流程是:当前线程首先尝试在指定的时间范围内获取锁。如果锁在这段时间内变得可用(即被其他线程释放),则线程成功获取锁并立即返回 true,随后可以继续执行其临界区代码。然而,如果指定的时间耗尽而锁仍未被释放,则方法将返回 false,表示获取锁失败,同时当前线程不会因此而被阻塞,可以继续执行其他任务或尝试其他恢复策略。tryLock(long time, TimeUnit unit) 的这一设计特性,不仅有效避免了线程因无限期等待锁而导致的死锁问题,还显著提升了系统的响应性和资源利用率。它允许开发者根据应用的具体需求,灵活调整等待时间,以平衡锁的获取效率与系统的整体性能。

tryLock() 方法同样支持无参数的直接调用模式。在此模式下,当前线程将尝试立即获取锁,若锁当前未被其他线程占用,则锁申请将立即成功,并返回 true。反之,若锁已被其他线程持有,则当前线程不会进入等待状态,而是直接返回 false。

tryLock(long time, TimeUnit unit) 官方注释如下:

在这里插入图片描述

在这里插入图片描述


由该注释可以得出,当调用 tryLock(long timeout, TimeUnit unit) 方法时,其行为遵循以下规则:

  1. 锁的可用性与中断检查:如果锁在给定的等待时间(timeout)内没有被其他线程占用,并且当前线程在此期间未被中断,则当前线程将成功获取锁,并立即返回 true。同时,锁的持有计数(对于可重入锁而言)将递增为 1。

  2. 公平锁策略:如果此锁被配置为使用公平排序策略,则即使锁当前可用,如果已有其他线程在等待获取锁,当前线程也不会立即获取锁。这与 tryLock()(无参数)的行为形成对比,后者会立即尝试获取锁而不考虑其他线程的等待状态。

  3. 锁的持有与重入:如果当前线程已经持有此锁,则再次调用 tryLock(long timeout, TimeUnit unit) 时,锁的保持计数将递增 1,并立即返回 true,无需等待。

  4. 等待与中断处理:如果锁由另一个线程持有,则当前线程将基于线程调度策略被禁用并进入休眠状态,直到满足以下条件之一:

    • 锁被当前线程成功获取。
    • 当前线程被其他线程中断。
    • 指定的等待时间已过。

    在等待过程中,如果当前线程在进入方法前已设置中断状态,或者在等待期间被中断,则将抛出 InterruptedException 异常,并清除当前线程的中断状态。

  5. 超时处理:如果指定的等待时间已过而锁仍未被当前线程获取,则方法返回 false。如果传入的等待时间小于或等于零,则该方法将不会进行任何等待,而是立即返回 false。

  6. 中断优先级:在此实现中,tryLock(long timeout, TimeUnit unit) 方法是一个显式的中断点。因此,在等待过程中,如果发生中断,线程将优先响应中断,而不是继续等待锁变得可用或重新获取锁,并立即报告中断事件的发生。

  7. 参数与返回值:

    • 参数:
      • timeout:等待锁的时间长度。
      • unit:timeout 参数的时间单位。
    • 返回值:如果锁在指定时间内变得可用并被当前线程成功获取,或者当前线程已经持有此锁,则返回 true;如果在指定时间内锁未被获取,则返回 false。
    • 异常:如果当前线程在等待过程中被中断,则抛出 InterruptedException。

3.限时锁的优势与注意事项

限时锁申请的优势

  • 有效预防死锁:通过设定明确的等待时间阈值,限时锁申请机制能够显著减少线程因长时间等待锁资源而无法释放,进而引发的死锁风险,增强了系统的稳定性和可靠性。
  • 优化系统响应能力:该机制确保线程在等待锁的过程中不会陷入无限期的阻塞状态,而是能够在指定的时间窗口内尝试获取锁。若超时未获取,则线程能够迅速释放CPU资源,转而执行其他任务或进行重试逻辑,从而显著提升了系统的响应速度和用户体验。
  • 高度灵活性:开发者能够根据具体业务场景的需求,灵活配置锁的等待时间,以适应不同的并发访问模式和性能要求。这种灵活性为系统设计提供了更多的优化空间,有助于构建更加高效、健壮的并发控制策略。

注意事项

  • 确保资源释放:在使用 tryLock(long time, TimeUnit unit) 方法时,建议在 finally 块中执行锁释放操作,以确保即使在捕获异常或发生错误的情况下,锁资源也能被正确释放,避免资源泄露和系统不稳定。
  • 适用场景考量:限时锁申请并非所有并发控制场景下的最优选择。它更适合于那些对操作响应时间有严格要求,且能够接受在高并发情况下部分操作因锁竞争失败而暂时无法完成的业务场景。
  • 合理设置等待时间:在配置限时锁申请的等待时间时,应综合考虑系统性能、资源利用率以及业务容忍度等多方面因素。过短的等待时间可能导致频繁的锁获取失败和重试,增加系统负担;而过长的等待时间则可能降低系统对突发高并发请求的响应速度,影响用户体验。因此,合理设定等待时间是实现高效并发控制的关键。

4.tryLock(long time, TimeUnit unit)案例

以下案例演示了ReentrantLock的tryLock(long time, TimeUnit unit)方法的使用。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {public static void main(String[] args) {Task task = new Task();Thread t1 = new Thread(task, "Thread-1");Thread t2 = new Thread(task, "Thread-2");// 确保第一个线程先开始t1.start();try {// 让第一个线程有机会先运行Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}t2.start();}static class Task implements Runnable {private static final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {String name = Thread.currentThread().getName();try {if (lock.tryLock(1, TimeUnit.SECONDS)) {System.out.println(name + " 成功获取锁,开始执行任务");// 模拟任务执行Thread.sleep(5000);System.out.println(name + " 任务执行完成,释放锁");} else {System.out.println(name + " 尝试在1秒内获取锁失败,锁被其他线程占用");}} catch (InterruptedException e) {System.out.println(name + " 被中断");// 重新设置中断状态Thread.currentThread().interrupt();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();System.out.println(name + " 释放锁");}}}}
}
  1. 创建任务和线程:ReentrantLockExample类中包含一个静态内部类Task,该类实现了Runnable接口。Task类有一个静态的ReentrantLock对象lock,这意味着所有Task实例共享同一个锁。然后,主方法中创建了两个Task实例的线程t1和t2。
  2. 线程启动顺序:首先启动线程t1,然后通过Thread.sleep(100)确保t1有机会先运行。之后启动线程t2。这样做的目的是模拟t1和t2可能同时尝试获取锁,但t1更有可能先获取锁的场景。
  3. 尝试获取锁:在Task的run方法中,每个线程尝试通过lock.tryLock(1, TimeUnit.SECONDS)在1秒内获取锁。如果成功,它将执行一些模拟任务(通过Thread.sleep(5000)模拟),并在完成后释放锁。如果线程在1秒内没有获取到锁(即锁被其他线程占用),它将打印一条消息表示获取锁失败。
  4. 异常处理和锁释放:在尝试获取锁和执行任务的过程中,如果线程被中断,则会捕获InterruptedException并重新设置中断状态。此外,无论线程是否成功获取锁或是否因异常而中断,最终都会检查当前线程是否持有锁,并相应地释放锁,这是为了避免死锁和确保锁资源被正确释放。
  5. 输出结果:根据线程执行的顺序和锁的状态,可能的输出包括:
    • 第一个线程(很可能是t1)将成功获取锁并开始执行任务。
    • 第二个线程(t2)将在尝试获取锁时失败,因为它被阻塞在tryLock方法中,直到锁被释放。
    • 当第一个线程完成任务并释放锁后,第二个线程(如果还在尝试获取锁)将有机会获取锁并执行任务。但在这个示例中,t2的tryLock方法可能已经超时并返回了false,所以它直接打印获取锁失败的消息。

运行结果:

在这里插入图片描述


总结

ReentrantLock 的 tryLock(long time, TimeUnit unit) 方法是处理并发访问共享资源时的一种高效且灵活的锁机制,通过合理使用限时锁申请,可以显著提高系统的并发性能和稳定性。本文主要介绍了ReentrantLock的限时锁申请,助力开发者构建高效稳定的并发应用。

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

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

相关文章

ICPC南京 C.拓扑 + 组合数Dp

题目 给出一棵有根树,问有多少个拓扑序满足 p i i p_ii pi​i 。 n ≤ 5000 n\leq 5000 n≤5000 样例 input1: 4 1 1 2 output1: 3 2 1 2 input2: 9 1 1 2 2 3 3 4 4 5 output2: 672 420 180 160 152 108 120 170 210题解 考虑设 f[x][i] 表示还未在序列中插入…

初始JavaEE篇——多线程(4):wait、notify,饿汉模式,懒汉模式,指令重排序

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:JavaEE 目录 wait、notify 方法 多线程练习 单例模式 饿汉模式 懒汉模式 指令重排序 wait、notify 方法 wait 和 我们前面学习的sleep…

在线预览 Word 文档

引言 随着互联网技术的发展,Web 应用越来越复杂,用户对在线办公的需求也日益增加。在许多业务场景中,能够直接在浏览器中预览 Word 文档是一个非常实用的功能。这不仅可以提高用户体验,还能减少用户操作步骤,提升效率…

C++ 优先算法 —— 查找总价格为目标值的两个商品(双指针)

目录 题目 :查找总价格为目标值的两个商品 1. 题目解析 2. 算法原理 Ⅰ 暴力枚举 Ⅱ 双指针算法 3. 代码实现 暴力枚举 双指针算法 题目 :查找总价格为目标值的两个商品 1. 题目解析 题目截图: 这道题的一个关键的地方,它先…

【操作系统】基于环形队列的生产消费模型

这篇博客的重点在于代码实现,理论部分请看CSDN 一、单生产单消费 1.环形队列的实现 单生产单消费的情况下,我们只需要维护生产者和消费者之间的互斥和同步关系即可 将环形队列封装成一个类:首先给出整体框架,接着会说明每一个…

【Android】Activity组件通信

文章目录 1.使用Intent传递数据2.使用Bundle传递复杂数据3.startActivityForResult 和 onActivityResult4.使用ViewModel共享数据 在Android中,Activity之间的通信是一个常见且重要的任务。以下是一些常用的方法来实现Activity之间的数据传递和通信: 1.使…

如何在Linux环境中的Qt项目中使用ActiveMQ-CPP

文章目录 代码1:消费者代码2:生成者 之前在Linux下的qt程序中使用activeMQ的时候也是用了很多时间去研究,本来想的是好好记录一下,但是当时顾着写代码。很多细节也不想再去走一遍了。大概写一下怎么使用就行了。注意:一…

Qt QCheckBox、QPushButton和QRadioButton详解

QCheckBox(复选框) 功能:QCheckBox用于创建一个复选框控件,允许用户从多个选项中选择多个。 属性: checkable:决定复选框是否可以被选中或取消选中。checked:表示复选框当前的选中状态&#…

6、显卡品牌分类介绍:技嘉 - 计算机硬件品牌系列文章

技嘉科技是一家以主板、‌显卡在业界缔造无以撼动的地位的科技公司,‌其核心理念是「‌技术创新、‌质量稳定」‌的高标准。‌技嘉专注于关键技术研发,‌其经营范围涵盖家用、‌商用、‌电竞等多元科技领域。‌通过应用突破性的专利技术,‌技…

自编以e为底的指数函数exp,性能接近标准库函数

算法描述: (1). 先做自变量x的范围检查,对于双精度浮点数,自变量不能超出(-1022ln2, 1024ln2)(-708.39, 709.78),否则exp(x)会溢出。对于单精度浮点数,自变量不能超出(-126ln2, 128ln2)(-87.33, 88.72). 自己使用此函数…

es安装拼音分词后Kibana出现内存错误

出现错误 今天在安装es的拼音分词器,并重启es容器后,登录Kibana无法使用,查询日志发现如下报错 Waiting until all Elasticsearch nodes are compatible with Kibana before starting saved objects migrations... | typelog timestamp2024…

前端react面试基础知识(II)

这些问题涵盖了 React 的很多核心概念和实际应用场景。下面是针对每个问题的详细回答: 1. **React 项目中,如何动态改变组件的 class 来切换样式?** 可以通过条件判断或者状态(state)来动态改变组件的 class。例如,使…

Day 42 || 完全背包、518. 零钱兑换 II 、 377. 组合总和 Ⅳ、70. 爬楼梯 (进阶)

完全背包 题目链接:卡码网第52题 思路:和之前01背包一样,但是物品可以无限放置,所以之前二维数组中的背包容量是倒序遍历的,现在可以正序遍历即可重复放入。 import java.util.Scanner; public class Main {public …

数据结构-二叉树中的递归

目录 前言 简单手撕二叉树 二叉树节点的求解 二叉树叶子节点的求解 二叉树高度 二叉树第K层节点的个数 二叉树查找值为X的节点 结束语 前言 在这里说声抱歉,好久没更新数据结构了,二叉树的相关内容还没有更新完,是小编的失职&#xff…

在基于AWS EC2的云端k8s环境中 搭建开发基础设施

中间件下载使用helm,这里部署的都是单机版的 aws-ebs-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:name: aws-ebs-storageclass provisioner: kubernetes.io/aws-ebs parameters:type: gp2 # 选择合适的 EBS 类型,如 gp2、io1…

Apache Calcite - 查询优化之自定义优化规则

RelOptRule简介 为了自定义优化规则,我们需要继承RelOptRule类。org.apache.calcite.plan.RelOptRule 是 Apache Calcite 中的一个抽象类,用于定义优化规则。优化规则是用于匹配查询计划中的特定模式,并将其转换为更优化的形式的逻辑。通过继…

2024网鼎杯青龙组wp:Crypto1

题目 附件内容如下 from Crypto.Util.number import * from secret import flag from Cryptodome.PublicKey import RSAp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,(p-1)*(q-1)) m bytes_to_long(flag) c pow(m,e,n) hint1 p >> (51…

Python 单元测试中的 Mocking 与 Stubbing:提高测试效率的关键技术

在软件开发过程中,单元测试是确保代码质量的重要环节。为了实现高效的单元测试,我们常常需要隔离待测试的代码与其外部依赖。这时候,Mocking(模拟)和 Stubbing(桩)技术就显得尤为重要。这两种技…

Golang | Leetcode Golang题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; type Solution struct {pre []int }func Constructor(w []int) Solution {for i : 1; i < len(w); i {w[i] w[i-1]}return Solution{w} }func (s *Solution) PickIndex() int {x : rand.Intn(s.pre[len(s.pre)-1]) 1return sort.Searc…

3D打印机 屏幕的固定挂钩断后的一次自己修复经历

引子 3D打印机的屏幕固定挂钩断了 这次确实不知道咋断的&#xff0c;这可咋办呢&#xff0c;到网上看了一下&#xff0c;一个屏幕要2佰多&#xff0c;有些小贵&#xff0c;要不就自己修修吧&#xff0c;打个挂钩按上&#xff0c;说干就干。 正文 freecad的设计图如下【其中各…