当前位置: 首页 > news >正文

ReentranLock(可重入锁)

一、ReentranLock

ReentranLock属于JUC并发工具包下的类,相当于 synchronized具备如下特点

● 可中断
● 可以设置超时时间
● 可以设置为公平锁(防止线程出现饥饿的情况)
● 支持多个条件变量

与 synchronized一样,都支持可重入

基本语法(synchronized在关键字级别保护临界区, reentrantLock是在对象的级别来保护临界区)

// 获取锁
reentrantLock.lock();
try {// 临界区
} finally {// 释放锁(无论是否出现异常,均会将锁释放)reentrantLock.unlock();
}

lock()与unlock()是成对出现的

1.1 可重入

可重入是指同一个线程对象如果首次获得这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁
如果是不可重入锁,那么第二次获得锁时,自身也会被锁挡住

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test")
public class Test {// 创建锁重入对象private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {// 加锁lock.lock();try {log.debug("enter  main");m1();} finally {// 解锁lock.unlock();}}public static void m1() {// 加锁lock.lock();try {log.debug("enter  m1");m2();} finally {// 解锁lock.unlock();}}public static void m2() {// 加锁lock.lock();try {log.debug("enter  m2");} finally {// 解锁lock.unlock();}}
}

运行结果:(锁重入成功)

在这里插入图片描述

1.2 可打断——lockInterruptibly

在等待锁的过程中其他线程可以用interruput()方法终止等待

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test")
public class Test {// 创建锁重入对象private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args)  {Thread t1=new Thread(()->{try {// 尝试获取锁,但可以被打断(如果没有别的线程竞争锁,此方法就会获取lock对象上的锁)/*若有竞争进入阻塞队列等待*/log.debug("尝试获得锁");lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("没有获取锁,返回");return;}try {log.debug("获取到锁");}finally {// 将锁释放掉lock.unlock();}},"t1");// 主线程先对其进行加锁后,t1线程才启动lock.lock();t1.start();// 主线程睡眠1s后打断t1Sleeper.sleep(1);t1.interrupt();}
}

运行结果:(成功打断t1线程)

1.3 锁超时

锁超时:在获取锁的过程中,如果其他线程持有锁一直未释放,去尝试获取锁的线程也不会死等,而是等待一段时间,若这段时间超过对方仍未释放锁,则放弃等待,获取锁失败

可打断属于一种被动的避免无限等待(死等)方式;而锁超时以主动的方式避免死等

1、无其他线程竞争锁:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test")
public class Test {// 创建锁重入对象private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {Thread t1 = new Thread(() -> {log.debug("尝试获得锁");// 尝试获取锁,返回值为布尔型  【成功:获取锁   失败:不可获得锁,不会进入阻塞队列等待】if (!lock.tryLock()) {           //失败则立刻返回(没有任何等待时间)log.debug("获取锁失败");    // falsereturn;}try {// 执行临界区代码log.debug("成功获取锁");} finally {lock.unlock();     // 释放锁}});}
}

运行结果:
在这里插入图片描述

2、存在其他线程竞争(立刻结束):

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test")
public class Test {// 创建锁重入对象private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {Thread t1 = new Thread(() -> {log.debug("尝试获得锁");// 尝试获取锁,返回值为布尔型  【成功:获取锁   失败:不可获得锁,不会进入阻塞队列等待】if (!lock.tryLock()) {log.debug("获取锁失败");    // falsereturn;}try {// 执行临界区代码log.debug("成功获取锁");} finally {lock.unlock();     // 释放锁}});// 主线程先对lock对象加锁lock.lock();log.debug("成功获取锁");t1.start();}
}

运行结果:
在这里插入图片描述

3、存在其他线程竞争(等待一段时间):尝试等待1s,1s内若主线程还未释放锁再结束
在这里插入图片描述

哲学家就餐问题便可以使用tryLock()解决

1.4 公平锁

ReentrantLock 默认是不公平的。当一个线程持有锁,其他线程就会进入阻塞队列等待,当锁的持有者释放锁时,阻塞队列中等待的线程会一拥而上,谁先争抢到锁谁便是Owner,而不会按进入阻塞队列的先后顺序先来先得

(通过查看源码发现其构造方法中有一个带boolean类型参数的方法,其参数fair默认为false,可以修改其布尔值保证其公平性)公平锁一般没有必要,会降低并发度

二、ReentranLock条件变量

2.1 简介

条件变量

synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待

ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比

● synchronized 是那些不满足条件的线程都在一间休息室等消息
● 而 ReentrantLock 支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤

使用要点:
● await 前需要获得锁
● await 执行后,会释放锁,进入 conditionObject 等待
● await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
● 竞争 lock 锁成功后,从 await 后继续执行

使用例子:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static cn.itcast.n2.util.Sleeper.sleep;@Slf4j(topic = "c.Test24")
public class Test24 {static final Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;static ReentrantLock ROOM = new ReentrantLock();// 等待烟的休息室(创建一个新的条件变量)static Condition waitCigaretteSet = ROOM.newCondition();// 等外卖的休息室(创建一个新的条件变量)static Condition waitTakeoutSet = ROOM.newCondition();public static void main(String[] args) {new Thread(() -> {// 尝试获取ReentrantLockROOM.lock();try {log.debug("有烟没?[{}]", hasCigarette);while (!hasCigarette) {log.debug("没烟,先歇会!");try {// 进入等烟休息室等待waitCigaretteSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以开始干活了");} finally {// 解锁ROOM.unlock();}}, "小南").start();new Thread(() -> {ROOM.lock();try {log.debug("外卖送到没?[{}]", hasTakeout);while (!hasTakeout) {log.debug("没外卖,先歇会!");try {waitTakeoutSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以开始干活了");} finally {ROOM.unlock();}}, "小女").start();// 送外卖线程sleep(1);new Thread(() -> {ROOM.lock();try {hasTakeout = true;// 唤醒线程waitTakeoutSet.signal();} finally {ROOM.unlock();}}, "送外卖的").start();// 送烟线程sleep(1);new Thread(() -> {ROOM.lock();try {hasCigarette = true;// 唤醒线程waitCigaretteSet.signal();} finally {ROOM.unlock();}}, "送烟的").start();}
}

运行结果:
在这里插入图片描述

相关文章:

ReentranLock(可重入锁)

一、ReentranLock ReentranLock属于JUC并发工具包下的类,相当于 synchronized具备如下特点 ● 可中断 ● 可以设置超时时间 ● 可以设置为公平锁(防止线程出现饥饿的情况) ● 支持多个条件变量 与 synchronized一样,都支持可重…...

Kafka 入门 (一)

Kafka 入门(一) Apache Kafka起源于LinkedIn,后来于2011年成为开源Apache项目,然后于2012年成为First-class Apache项目。Kafka是用Scala和Java编写的。 Apache Kafka是基于发布订阅的容错消息系统。 它是快速,可扩展…...

linux内核开发入门二(内核KO模块介绍、开发流程以及注意事项)

linux内核开发入门二(内核KO模块介绍、开发流程以及注意事项) 一、什么是内核模块 内核模块:ko模块(Kernel Object Module)是Linux内核中的可加载模块,它可以动态地向内核添加功能。在运行时,可…...

设计模式(十七)----行为型模式之模板方法模式

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为&…...

【嵌入式Linux内核驱动】01_内核模块

内核模块 宏内核&微内核 微内核就是内核中的一部分功能放到应用层 内核小,精简,可扩展性好,安全性好 相互之间通信损耗多 内核模块 Linux是宏内核操作系统的典型代表,所有内核功能都整体编译到一起,优点是效…...

Spring——数据源对象管理和Spring加载properties文件

前面一直都是在管理自己内部创建的对象&#xff0c;这个是管理外部的对象。 这里先使用阿里巴巴的druid来演示。需要在pom.xml中添加如下的依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1…...

Zeek安装、使用与压力测试

Zeek安装与压力测试Zeek安装、简单使用与压力测试环境Zeek安装zeek简单运行安装PF_RING修改Zeek配置文件&#xff0c;使用PF_RING&#xff0c;实现集群流量压力测试查看zeek日志Zeek安装、简单使用与压力测试 科研需要&#xff0c;涉及到Zeek的安装、使用和重放流量压力测试评…...

【javaEE初阶】第三节.多线程 (进阶篇 ) 死锁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、"死锁"出现的典型场景二、产生 "死锁" 的必要条件 三、解决 "死锁" 问题的办法 总结前言 今天对于多线程进阶的学习&#…...

基于密集连接的轻量级卷积神经网络,用于使用边云架构的露天煤矿服务识别

遥感是快速检测非法采矿行为的重要技术工具。由于露天煤矿的复杂性&#xff0c;目前关于露天煤矿自动开采的研究较少。基于卷积神经网络和Dense Block&#xff0c;我们提出了一种用于从Sentinel-2遥感图像中提取露天煤矿区域的轻量级密集连接网络-AD-Net&#xff0c;并构建了三…...

无刷高速风筒方案介绍--【PCBA方案】

疫情三年过去&#xff0c;春节后&#xff0c;一个新的开始&#xff0c;大家满怀希望畅谈今年好气象。 三年来一波一波的封城、隔离、核酸&#xff0c;经济压抑到了无以复加的地步&#xff0c;也导致了诸多社会问题的出现。消费力被磨平&#xff0c;人们小心翼翼的生活。 常跟…...

花括号展开II[栈模拟dfs]

栈模拟dfs前言一、花括号展开II二、栈模拟dfs总结参考资料前言 递归调用&#xff0c;代码非常的简洁。但是可以通过显式栈来模拟栈中的内容&#xff0c;锻炼自己的代码能力&#xff0c;清楚知道栈帧中需要的内容。 一、花括号展开II 二、栈模拟dfs 每碰到一个左括号&#xf…...

神经网络分类任务(手写数字识别)

1.Mnist分类任务 网络基本构建与训练方法&#xff0c;常用函数解析 torch.nn.functional模块 nn.Module模块 学习方法&#xff1a;边用边查&#xff0c;多打印&#xff0c;duogua 使用jupyter的优点&#xff0c;可以打印出每一个步骤。 2.读取数据集 自动下载 %matplotl…...

FCN网络(Fully Convolutional Networks)

首个端到端的针对像素级预测的全卷积网络 原理&#xff1a;将图片进行多次卷积下采样得到chanel为21的特征层&#xff0c;再经过上采样得到和原图一样大的图片&#xff0c;最后经过softmax得到类别概率值 将全连接层全部变成卷积层&#xff1a;通常的图像分类网络最后几层是全…...

随想录二刷Day15——二叉树

文章目录二叉树2. 递归遍历二叉树3. 二叉树的迭代遍历4. 二叉树的统一迭代法二叉树 2. 递归遍历二叉树 144. 二叉树的前序遍历 class Solution { public:vector<int> preorderTraversal(TreeNode* root) {vector<int> result;preorder(root, result);return res…...

docker-compose部署kafka服务时如何同时允许内外网访问?

背景 最近在学习kafka相关知识&#xff0c;需要搭建自己的kafka环境。综合考虑后决定使用docker-compose来管理维护这个环境。 docker-compose.yml Bitnami的yml文件就很不错&#xff0c;这里直接拿来用了。 version: "2"services:zookeeper:image: docker.io/bi…...

数据结构刷题(二十):17电话号码的字母组合、39组合总和、40组合总和II

一、电话号码的字母组合题目链接思路&#xff1a;回溯三部曲。确定回溯函数参数&#xff1a;题目中给的 digits&#xff0c;还要有一个参数就是int型的index&#xff08;记录遍历第几个数字&#xff0c;就是用来遍历digits的&#xff0c;同时也代表了递归的深度&#xff09;&am…...

Java面试总结(五)

sleep() 方法和 wait() 方法对比 相同点 两者都可以暂停线程的执行&#xff1b;两者都可以响应中断。 不同点 sleep()方法不会释放锁&#xff0c;wait()方法会释放锁&#xff1b; sleep()方法主要用于暂停线程的执行&#xff0c;wait()方法主要用于线程之间的交互/通信&…...

三维人脸实践:基于Face3D的渲染、生成与重构 <二>

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 3DMM方法&#xff0c;基于平均人脸模型&#xff0c;可广泛用于基于关键点的人脸生成、位姿检测以及渲染等&#xff0c;能够快速实现人脸建模与渲染。推…...

在linux上部署Java项目

在Linux部署Java环境 要是想要部署java web程序,首先要配置环境 jdk tomcat mysql 安装jdk 推荐的方法是使用yum直接安装openjdk(开源的,与官方的jdk功能差不多),目前使用的最多的就是jdk8系列 yum list | grep jdk 在源上搜索所有关于jdk的文件 devel表示development的意思…...

线性表的接口

线性表的实现方式 顺序表 顺序表是一种线性表的实现方式&#xff0c;它是用一组地址连续的存储单元依次存储线性表中的数据元素&#xff0c;使得逻辑上相邻的元素在物理上也相邻⁴。顺序表可以用数组来实现&#xff0c;它的优点是可以快速定位第几个元素&#xff0c;但是缺点…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...

C#中用于控制自定义特性(Attribute)

我们来详细解释一下 [AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)] 这个 C# 属性。 在 C# 中&#xff0c;Attribute&#xff08;特性&#xff09;是一种用于向程序元素&#xff08;如类、方法、属性等&#xff09;添加元数据的机制。Attr…...

开源 vGPU 方案:HAMi,实现细粒度 GPU 切分

本文主要分享一个开源的 GPU 虚拟化方案&#xff1a;HAMi&#xff0c;包括如何安装、配置以及使用。 相比于上一篇分享的 TimeSlicing 方案&#xff0c;HAMi 除了 GPU 共享之外还可以实现 GPU core、memory 得限制&#xff0c;保证共享同一 GPU 的各个 Pod 都能拿到足够的资源。…...

慢慢欣赏linux 之 last = switch_to(prev, next)分析

last switch_to(prev, next); 为什么需要定义last作为调用switch_to之前的prev的引用 原因如下&#xff1a; struct task_struct * switch_to(struct task_struct *prev,struct task_struct *next) {... ...return cpu_switch_to(prev, next);> .global cpu_switch_tocpu_…...