深入探索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,一经查实,立即删除!

相关文章

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

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

在线预览 Word 文档

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

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

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

Qt QCheckBox、QPushButton和QRadioButton详解

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

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

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

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

目录 前言 简单手撕二叉树 二叉树节点的求解 二叉树叶子节点的求解 二叉树高度 二叉树第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…

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…

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的设计图如下【其中各…

PHP合成图片,生成海报图,poster-editor使用说明

之前写过一篇使用Grafika插件生成海报图的文章&#xff0c;但是当我再次使用时&#xff0c;却发生了错误&#xff0c;回看Grafika文档&#xff0c;发现很久没更新了&#xff0c;不兼容新版的GD&#xff0c;所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…

Java基于微信小程序的美食推荐系统(附源码,文档)

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Linux的IP网路命令: 用于显示和操作网络接口(网络设备)的命令ip link详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、常用参数 4、获取帮助 三、示例 1. 显示所有网络接口的信息 &#xff08;1&#xff09;命令 &#xff08;2&#xff09;输出示例 &#xff08;3&#xff09;实际操作 2. 启动网络接口 3. 停止网络接口 4. 更改…

C语言 | Leetcode C语言题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; int countArrangement(int n) {int f[1 << n];memset(f, 0, sizeof(f));f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num __builtin_popcount(mask);for (int i 0; i < n; i) {if (mask & (1 <<…

SpringBoot篇(自动装配原理)

目录 一、自动装配机制 1. 简介 2. 自动装配主要依靠三个核心的关键技术 3. run()方法加载启动类 4. 注解SpringBootApplication包含了多个注解 4.1 SpringBootConfiguration 4.2 ComponentScan 4.3 EnableAutoConfiguration 5. SpringBootApplication一共做了三件事 …

3D Gaussian Splatting代码详解(二):模型构建

3 模型构建 gaussians GaussianModel(dataset.sh_degree) 3.1 初始化函数 __init__ 构造函数 构造函数 __init__ 的主要作用是初始化 3D 高斯模型的各项参数和激活函数&#xff0c;用于生成 3D 空间中的高斯表示。 初始化球谐函数的参数&#xff1a; self.active_sh_degre…

如何在 linux 中使用 /etc/fstab 挂载远程共享 ?

在 Linux 领域&#xff0c;高效的管理文件系统和数据存储对于用户和管理员来说&#xff0c;是一项基本技能。 有一种特别有用的技术涉及自动建立远程共享&#xff0c;允许无缝访问网络存储&#xff0c;就好像是本地的一样。 本指南将引导您完成使用 /etc/fstab 文件以自动远程…

iOS用rime且导入自制输入方案

iPhone 16 的 cantonese 只能打传统汉字&#xff0c;没有繁简转换&#xff0c;m d sh d。考虑用「仓」输入法 [1] 使用 Rime 打字&#xff0c;且希望导入自制方案 [2]。 仓输入法有几种导入方案的方法&#xff0c;见 [3]&#xff0c;此处记录 wifi 上传法。准备工作&#xff1…

ts:常见的运算符

ts&#xff1a;常见的运算符 1 主要内容说明2 表格2.1 算数运算符2.2 赋值运算符2.3 比较运算符2.4 逻辑运算符2.5 位运算符2.6 三元运算符 3 例子3.1 位运算符3.1.1 源码1 &#xff08;位运算符&#xff09;3.1.2 源码1运行效果 3.结语4.定位日期 1 主要内容说明 ts中的各种运…

unity搭建场景学习

unity搭建场景学习 创建场景创建gameobject创建材质&#xff0c;用于给gameobject上色拖拽材质球上色上色原理设置多个材质方式设置贴图的方式 效果设置光滑度一些预览设置菜单渲染模型与碰撞模型网格渲染参数1. materials(材质)2. lighting(光照)3. reflection probes(反射探针…