JUC:ReentrantLock(可打断、锁超时、多条件变量)

2024-03-31 19:52

本文主要是介绍JUC:ReentrantLock(可打断、锁超时、多条件变量),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • ReentrantLock
    • 特点
    • 基本语法
    • 可重入
    • 可打断(避免死等、被动)
    • 锁超时(避免死等、主动)
    • 公平锁
    • 多个条件变量

ReentrantLock

翻译:可重入锁

特点

  • 可中断
  • 可设置超时时间(不会一直等待锁)
  • 可设置为公平锁(防止线程饥饿)
  • 支持多个条件变量
  • 与 synchronized 一样可重入

基本语法

// 获取锁
reentrantLock.lock();
try {// 临界区
} finally {// 释放锁reentrantLock.unlock();
}

可重入

同一个线程如果已经拥有了锁,可以再次获得这把锁。

@Slf4j(topic = "c.Test3")
public class st3 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {lock.lock(); // 加锁try {log.debug("enter m1");m1();}finally {lock.unlock(); // 解锁}}public static void m1 () {lock.lock();try {log.debug("enter m2");m2();} finally {lock.unlock();}}public static void m2 () {lock.lock();try {log.debug("m2获取了锁");} finally {lock.unlock();}}
}

在这里插入图片描述

可打断(避免死等、被动)

lockInterruptibly 可打断锁,在尝试获取锁进入阻塞队列中,被打断时,可以停止等待锁。

 public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {try {log.debug("t1线程尝试获取锁");lock.lockInterruptibly(); // 尝试获取锁。若有竞争,进入阻塞队列,可被打断} catch (InterruptedException e) {e.printStackTrace();log.debug("等待锁的时候被打断了,停止等待,返回");return;}try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("主线程获得了锁");t1.start();sleep(1);log.debug("打断 t1");t1.interrupt();lock.unlock();}

锁超时(避免死等、主动)

无参数:获取锁,若没获取到,立即失败,返回false。

@Slf4j(topic = "c.Test3")
public class st3 {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");if (!lock.tryLock()) {log.debug("t1获取锁立刻失败,返回");return;}try {log.debug("t1获得到了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("主线程获得了锁");t1.start();sleep(2);lock.unlock();}
}

在这里插入图片描述

可以主动设置等待时间。

@Slf4j(topic = "c.Test3")
public class st3 {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");try {if (!lock.tryLock(1, TimeUnit.SECONDS)) {log.debug("尝试获取锁1s,失败,返回");return;}} catch (InterruptedException e) { // 被打断,应该也returne.printStackTrace();}try {log.debug("t1获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("主线程获得了锁");t1.start();sleep(1);lock.unlock();}
}

主线程1ms后释放了锁,t1线程还在1s的等待中,可以获得锁,若时等待超过1s那就不再尝试获取锁了。

在这里插入图片描述

若主线程沉睡1s的情况:

sleep(1000);

在这里插入图片描述

公平锁

ReentrantLock默认也是不公平锁,但是可以修改。

公平: 先来先执行
不公平: 随机有机会

这里就给出如何修改

ReentrantLock lock = new ReentrantLock(true);  // 公平
ReentrantLock lock = new ReentrantLock(false); // 不公平  

一般是不设置的,会降低并发度,所以就不给出例子了。

多个条件变量

  • ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的。
  • synchronized只能全部都唤醒,而ReentrantLock可以根据条件唤醒对应线程。
Condition waitCigaretteQueue = lock.newCondition(); // 条件
waitCigaretteQueue.await(); // 因为此原因进入等待  

比如,在操作系统里,有一个很经典的问题,爸爸向盘子里放苹果,妈妈向盘子里放香蕉,儿子只吃苹果,女儿只吃香蕉,盘子可以放多个水果,但每种最多一个。

如果我么只用sync锁,那么就只能有一个条件,如果只有爸爸向盘子里放了苹果,那么他会唤醒儿子和女儿,但其实只唤醒儿子即可。
所以利用ReentrantLock多条件,因为沉睡的条件不同,我么就可以对应的唤醒我们想要的一些线程。
这里例子是用的烟和早餐,一样的。

@Slf4j(topic = "c.Test4")
public class st4 {static ReentrantLock lock = new ReentrantLock();static Condition waitCigaretteQueue = lock.newCondition(); //因为等烟等烟,进入等待的阻塞线程队列static Condition waitTokeoutQueue = lock.newCondition(); //因为等早餐进入阻塞队列static boolean hasCigarette = false;static boolean hasTokeout = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {lock.lock();try {while (!hasCigarette) {try {waitCigaretteQueue.await(); // 因为等烟,进入等待} catch (InterruptedException e) {e.printStackTrace();}}log.debug("等到了烟");} finally {lock.unlock(); // 解锁}}).start();new Thread(() -> {try {lock.lock();while (!hasTokeout) {try {waitTokeoutQueue.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("等到了它的早餐");} finally {lock.unlock();}}).start();sleep(1);sendBreakfast();sleep(1);sendCigarette();}private static void sendCigarette() {lock.lock();try {log.debug("送烟来的了");hasCigarette = true;waitCigaretteQueue.signal();} finally {lock.unlock();}}private static void sendBreakfast() {lock.lock();try {log.debug("送早餐的来了");hasTokeout = true;waitTokeoutQueue.signal();} finally {lock.unlock();}}
}

在这里插入图片描述

这篇关于JUC:ReentrantLock(可打断、锁超时、多条件变量)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865195

相关文章

Feign Client超时时间设置不生效的解决方法

《FeignClient超时时间设置不生效的解决方法》这篇文章主要为大家详细介绍了FeignClient超时时间设置不生效的原因与解决方法,具有一定的的参考价值,希望对大家有一定的帮助... 在使用Feign Client时,可以通过两种方式来设置超时时间:1.针对整个Feign Client设置超时时间

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结