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

相关文章

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例

MySQL 筛选条件放 ON后 vs 放 WHERE 后的区别解析

《MySQL筛选条件放ON后vs放WHERE后的区别解析》文章解释了在MySQL中,将筛选条件放在ON和WHERE中的区别,文章通过几个场景说明了ON和WHERE的区别,并总结了ON用于关... 今天我们来讲讲数据库筛选条件放 ON 后和放 WHERE 后的区别。ON 决定如何 "连接" 表,WHERE

MybatisPlus中几种条件构造器运用方式

《MybatisPlus中几种条件构造器运用方式》QueryWrapper是Mybatis-Plus提供的一个用于构建SQL查询条件的工具类,提供了各种方法如eq、ne、gt、ge、lt、le、lik... 目录版本介绍QueryWrapperLambdaQueryWrapperUpdateWrapperL

Nginx内置变量应用场景分析

《Nginx内置变量应用场景分析》Nginx内置变量速查表,涵盖请求URI、客户端信息、服务器信息、文件路径、响应与性能等类别,这篇文章给大家介绍Nginx内置变量应用场景分析,感兴趣的朋友跟随小编一... 目录1. Nginx 内置变量速查表2. 核心变量详解与应用场景3. 实际应用举例4. 注意事项Ng

浅析Python中如何处理Socket超时

《浅析Python中如何处理Socket超时》在网络编程中,Socket是实现网络通信的基础,本文将深入探讨Python中如何处理Socket超时,并提供完整的代码示例和最佳实践,希望对大家有所帮助... 目录开篇引言核心要点逐一深入讲解每个要点1. 设置Socket超时2. 处理超时异常3. 使用sele

Python之变量命名规则详解

《Python之变量命名规则详解》Python变量命名需遵守语法规范(字母开头、不使用关键字),遵循三要(自解释、明确功能)和三不要(避免缩写、语法错误、滥用下划线)原则,确保代码易读易维护... 目录1. 硬性规则2. “三要” 原则2.1. 要体现变量的 “实际作用”,拒绝 “无意义命名”2.2. 要让

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏