ReentrantLock

文章目录

    • 1. 简介
    • 2. 可重入
    • 3. 可中断
    • 4. 锁超时
    • 5. 使用可重入锁解决哲学家就餐问题
    • 6. 公平锁
    • 7. 条件变量

1. 简介

ReentrantLock也称为可重入锁,相对于synchronized它有如下特点:

  • 可中断:synchronized获取了锁,除非线程自己结束,中途是不能取消锁的使用的
  • 可以设置超时时间:synchronized等待锁的线程在管程的Entry-List中等待,直到获取锁,但ReentrantLock可以设置超时时间,如果超过了等待时间,线程可以结束等待过程
  • 可以设置公平锁:公平锁可以防止饥饿
  • 可以支持多个条件变量:所以条件变量就是管程的wait-set,synchronized只有一个wait-set而ReentrantLock可以根据不同的等待条件设置多个wait-set
    与Synchronized一样,都支持可重入

基本语法

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

2. 可重入

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

@Slf4j
public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args){//获取锁reentrantLock.lock();try {log.debug("进入主方法");//再次获取锁m1();}finally {reentrantLock.unlock();}}public static void m1(){reentrantLock.lock();try {log.debug("进入m2");m2();}finally {reentrantLock.unlock();}}public static void m2(){reentrantLock.lock();try {log.debug("进入m3");}finally {reentrantLock.unlock();}}}final class Chopsticks{String name;public Chopsticks(String name){this.name=name;}@Overridepublic String toString() {return "chopsticks{" +"name='" + name + '\'' +'}';}
}

在这里插入图片描述
所以ReentrantLock是可重入的

3. 可中断

ReentrantLock中的lockInterruptibly()方法使得线程可以在被阻塞时响应中断,比如一个线程t1通过lockInterruptibly()方法获取到一个可重入锁,并执行一个长时间的任务,另一个线程通过interrupt()方法就可以立刻打断t1线程的执行,来获取t1持有的那个可重入锁。而通过ReentrantLock的lock()方法或者Synchronized持有锁的线程是不会响应其他线程的interrupt()方法的,直到该方法主动释放锁之后才会响应interrupt()方法。

下面演示了lockInterruptibly()的使用

public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args){Thread t1=new Thread(()->{try{log.debug("尝试获取锁");reentrantLock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("没有获取锁");return;} try {log.debug("获取锁成功");}finally {reentrantLock.unlock();}},"线程1");t1.start();}
}

下面模拟有其它线程打断的情况

@Slf4j
public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args){Thread t1=new Thread(()->{try{Thread.sleep(1000);log.debug("尝试获取锁");reentrantLock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("没有获取锁");return;}try {log.debug("获取锁成功");}finally {reentrantLock.unlock();}},"线程1");t1.start();Thread t2=new Thread(()->{reentrantLock.lock();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}t1.interrupt();},"线程2");t2.start();}
}

可以发现t2打断了t1无限制等待锁,这种机制可以避免死锁的发生(注意通过lock方法获取的锁是不可打断的)

4. 锁超时

ReentrantLock还具备锁超时的能力,调用tryLock(long timeout, TimeUnit unit)方法,在给定时间内获取锁,获取不到就退出,这也是synchronized没有的功能。
方式一

@Slf4j
public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args){Thread t1=new Thread(()->{//尝试获取锁if (!reentrantLock.tryLock()) {log.debug("获取不到锁");return;}try {log.debug("获得锁了");}finally {reentrantLock.unlock();}},"线程1");reentrantLock.lock();log.debug("获取到锁了");t1.start();}
}

在这里插入图片描述
方式二

@Slf4j
public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{//尝试获取锁try {if (!reentrantLock.tryLock(1, TimeUnit.SECONDS)) {log.debug("获取不到锁:{}",System.currentTimeMillis());return;}} catch (InterruptedException e) {e.printStackTrace();}try {log.debug("获得锁了:{}",System.currentTimeMillis());}finally {reentrantLock.unlock();}},"线程1");reentrantLock.lock();log.debug("获取到锁:{}",System.currentTimeMillis());Thread.sleep(500);reentrantLock.unlock();t1.start();}
}

在这里插入图片描述

5. 使用可重入锁解决哲学家就餐问题

哲学家就餐问题
解决哲学家就餐问题的一种思路就是让哲学家按照顺序拿到筷子,即顺序加锁。但这种方式带来的问题就是,可能让有些线程陷入到饥饿状态,这里我们使用ReentrantLock来解决哲学家问题时,就是让每个哲学家等待筷子时有一点的时间,如果在指定时间内没有获取到筷子,就放弃手里的筷子,然后进入思考状态。具体代码实现如下:

@Slf4j
public class Hello{public static void main(String[] args){Chopsticks c1=new Chopsticks("筷子1");Chopsticks c2=new Chopsticks("筷子2");Chopsticks c3=new Chopsticks("筷子3");Chopsticks c4=new Chopsticks("筷子4");Chopsticks c5=new Chopsticks("筷子5");new Philosopher(c1,c2,"哲学家1").start();new Philosopher(c2,c3,"哲学家2").start();new Philosopher(c3,c4,"哲学家3").start();new Philosopher(c4,c5,"哲学家4").start();new Philosopher(c5,c1,"哲学家5").start();}}final class Chopsticks extends ReentrantLock{String name;public Chopsticks(String name){this.name=name;}@Overridepublic String toString() {return "chopsticks{" +"name='" + name + '\'' +'}';}
}@Slf4j
final class Philosopher extends Thread{Chopsticks left;Chopsticks right;@Overridepublic void run() {while(true){if (left.tryLock()) {try{if (right.tryLock()) {try{log.debug("eat");}finally {right.unlock();}}}finally {left.unlock();}}}}public void eat() throws InterruptedException {log.debug("两只筷子都有了,开始吃了");Thread.sleep(1000);}public Philosopher(Chopsticks left, Chopsticks right,String name){super(name);this.left=left;;this.right=right;}}

从结果可以看出,所有的哲学家都吃到了饭,且不会陷入死锁状态。

6. 公平锁

我们知道在sychronized锁住一个对象时,其它线程只能在管程中的Entry-list中被阻塞等待。当释放锁时,会随机选择一个等待线程执行。这种不考虑先来后到的情况就是说明这种锁时不公平的。ReentranLokc默认是一种不公平锁,如下:

public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock();public static void main(String[] args) throws InterruptedException {//主线程获取锁reentrantLock.lock();for (int i = 0; i < 5; i++) {new Thread(()->{//尝试获取锁try {reentrantLock.lock();log.info("获取到锁了");}finally {reentrantLock.unlock();}},"t"+i).start();}Thread.sleep(1000);reentrantLock.unlock();}}

在这里插入图片描述
由锁的获取顺序可以知道,ReetranLock不是公平锁

我们可以通过创建ReentranLock时闯入一个boolean指来表示是否开启公平锁功能。

@Slf4j
public class Hello{private static ReentrantLock reentrantLock=new ReentrantLock(true);public static void main(String[] args) throws InterruptedException {//主线程获取锁reentrantLock.lock();for (int i = 0; i < 5; i++) {new Thread(()->{//尝试获取锁try {Thread.sleep(100);reentrantLock.lock();log.info("获取到锁了");} catch (InterruptedException e) {throw new RuntimeException(e);} finally {reentrantLock.unlock();}},"t"+i).start();}Thread.sleep(1000);reentrantLock.unlock();}}

公平锁设计的初衷时解决线程饥饿问题,但这个问题使用tryLock()解决效果更好,公平锁一般没有必要,会降低并发度。

7. 条件变量

synchronized中也有条件变量,就是Monitor锁中的waitSet,当条件不满足时线程进入waitSet等待,ReetranLock的条件变量比synchronized强大之处在于,它时支持多个条件变量的:

  • synchronized就是那些不满足条件的线程都在一个waitset中等待
  • ReetranLock支持多个waitset,根据不同的条件会有不同的waitset

使用流程

  • await前获得锁
  • await执行后,会释放锁,进入conditionObject等待
  • await的线程被唤醒(或打断、或超时)去重新竞争锁
  • 竞争锁成功后,从await后面代码继续执行

下面用代码演示:

public static void main(String[] args) throws InterruptedException {//创建两个条件变量Condition condition1 = reentrantLock.newCondition();Condition condition2 = reentrantLock.newCondition();reentrantLock.lock();condition1.await();//唤醒condition1中的某个线程condition1.signal();//唤醒condition1中的所有线程condition1.signalAll();}

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

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

相关文章

本周Github有趣的项目、工具和库:Radius等

Github有趣的项目、工具和库 1、Radius Radius 是一个开源云原生应用程序平台&#xff0c;使开发人员和支持他们的运营商能够跨公共云和私有基础设施定义、部署和协作云原生应用程序 不仅仅是 Kubernetes Radius 通过支持 Kubernetes 等成熟技术、Terraform 和 Bicep 等现有基…

自然语言处理中的文本聚类:揭示模式和见解

一、介绍 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;文本聚类是一种基本且通用的技术&#xff0c;在信息检索、推荐系统、内容组织和情感分析等各种应用中发挥着关键作用。文本聚类是将相似文档或文本片段分组为簇或类别的过程。这项技术使我们能够发现隐藏的…

JavaWeb篇_02——服务器简介及Tomcat服务器简介

服务器简介 硬件服务器的构成与一般的PC比较相似&#xff0c;但是服务器在稳定性、安全性、性能等方面都要求更高&#xff0c;因为CPU、芯片组、内存、磁盘系统、网络等硬件和普通PC有所不同。软件服务器&#xff08;英文名称Server&#xff09;&#xff0c;也称伺服器。指一个…

字符加密A--E,B-F,W--A

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为选择结构编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 二、题目分析 三、解题 程序运行代码 #include<stdio.h> int main(){char c;cgetchar();if(c>a&&…

国潮力量:中国年轻一代如何通过跨境电商推广中国文化

中国国潮&#xff0c;或称国民潮流&#xff0c;是中国年轻一代通过各种方式&#xff0c;如时尚、音乐、文化和艺术&#xff0c;展示他们的文化身份和创新的表达方式。国潮不仅在国内走红&#xff0c;还在国际市场上崭露头角。 其中&#xff0c;跨境电商在国潮的传播和推广中发…

服务器数据恢复—云服务器mysql数据库表被truncate的数据恢复案例

云服务器数据恢复环境&#xff1a; 阿里云ECS网站服务器&#xff0c;linux操作系统mysql数据库。 云服务器故障&#xff1a; 在执行数据库版本更新测试时&#xff0c;在生产库误执行了本来应该在测试库执行的sql脚本&#xff0c;导致生产库部分表被truncate&#xff0c;还有部…

等保评测是什么意思

等保评测是一种信息安全评估标准&#xff0c;是国家信息安全管理机构为确保信息安全而对信息系统的安全性能进行定期评估的行为。它主要用于评估网络安全的实现情况&#xff0c;包括组织和技术。 等保评测具有系统性和综合性&#xff0c;能够及时发现网络安全风险&#xff0c;…

git reset 与 git revert 用法(回退远程提交)

一、git reset 回退到倒数第二笔提交&#xff0c;最强制-f提交 $ git log commit 11111111111111111111111111111111111 (HEAD -> U1101_6113_GMS, origin/U1101_6113_GMS) Author: xw-server3 <111sunmontech.com> Date: Wed Nov 8 10:35:01 2023 0800lyz 回退瓶…

unity - Blend Shape - 变形器 - 实践

文章目录 目的Blend Shape 逐顶点 多个混合思路Blender3Ds maxUnity 中使用Project 目的 拾遗&#xff0c;备份 Blend Shape 逐顶点 多个混合思路 blend shape 基于&#xff1a; vertex number, vertex sn 相同&#xff0c;才能正常混合、播放 也就是 vertex buffer 的顶点数…

云闪付app拉新 地推和网推百搭项目 升级涨价啦 附一手渠道

云闪付为银联的支付产品&#xff0c;在地推网推拉新项目市场里也是比较稳定的项目 可以通过”聚量推客“ 渠道申请推广 今天平台拿到了更好的政策价格 价格再次上涨&#xff0c;地推和网推的小伙伴们可以申请推广哦&#xff0c;百搭项目

从Hugging Face下载数据测试whisper、fast_whisper耗时

时长比较短的音频&#xff1a;https://huggingface.co/datasets/PolyAI/minds14/viewer/en-US 时长比较长的音频&#xff1a;https://huggingface.co/datasets/librispeech_asr?row8 此次测试过程暂时只使用比较短的音频 使用fast_whisper测试 下载安装&#xff0c;参考官方…

51单片机-串口通信

文章目录 前言1.基础介绍2.串口实战3.4. 前言 1.基础介绍 常见1&#xff0c;2&#xff0c;3,电源 常用方式1 fosc外部晶振 2.串口实战 3. 4.

【脑机接口 算法】EEGNet: 通用神经网络应用于脑电信号

EEGNet: 神经网络应用于脑电信号 中文题目论文下载&#xff1a;算法程序下载&#xff1a;摘要1 项目介绍2 EEGNet网络原理2.1EEGNet原理架构2.2FBCCA 算法2.3自适应FBCCA算法 3EEGNet网络实现4结果 中文题目 论文下载&#xff1a; DOI: 算法程序下载&#xff1a; 地址 摘要…

12. 一文快速学懂常用工具——docker 命令

本章讲解知识点 Docker 引擎Docker 常用命令Docker 生命周期详解Containerd 与 Docker 命令对比本专栏适合于软件开发刚入职的学生或人士,有一定的编程基础,帮助大家快速掌握工作中必会的工具和指令。本专栏针对面试题答案进行了优化,尽量做到好记、言简意赅。如专栏内容有错…

Linux下yum源配置实战

一、Linux下软件包的管理 1、软件安装方式 ① RPM包管理&#xff08;需要单独解决依赖问题&#xff09; ② YUM包管理&#xff08;需要有网络及YUM仓库的支持&#xff0c;会自动从互联网下载软件&#xff0c;自动解决依赖&#xff09; ③ 源码安装&#xff08;安装过程比较…

快速搭建开源分布式任务调度系统DolphinScheduler并远程访问

使用Docker部署开源分布式任务调度系统DolphinScheduler 文章目录 使用Docker部署开源分布式任务调度系统DolphinScheduler前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinSchedu…

windows上 Nexus 批量上传 maven依赖npm依赖

windows上 Nexus 批量上传 maven依赖/npm依赖 前言&#xff1a;windows系统上要有git环境&#xff0c;不然sh文件执行不了 1.批量上传maven依赖 设置脚本&#xff0c;把脚本放在依赖包的根目录执行&#xff0c;脚本名为upload.sh #!/bin/bash# 定义变量 while getopts &quo…

Kotlin文件和类为什么不是一对一关系

在Java中&#xff0c;一个类文件的public类名必须和文件名一致&#xff0c;如何不一致就会报异常&#xff0c;但是在kotlin的文件可以和类名一致&#xff0c;也可以不一致。这种特性&#xff0c;就跟c有点像&#xff0c;毕竟c的.h 和 .cpp文件是分开的。只要最终编译的时候对的…

MSSQL 配置ORACLE ​链接服务器

在有些场景&#xff0c;我们需要整合其他异构数据库的数据。我们可以使用代码去读取&#xff0c;经过处理后&#xff0c;再将数据保存到MSSQL数据库中。如果数据量比较大&#xff0c;但处理的逻辑并不复杂的情况下&#xff0c;这种方式就不是最好的办法。这时可以使用使用链接服…

Java中单例模式

什么是单例模式&#xff1f; 1. 构造方法私有化 2. 静态属性指向实例 3. public static的 getInstance方法&#xff0c;返回第二步的静态属性 饿汉式是立即加载的方式&#xff0c;无论是否会用到这个对象&#xff0c;都会加载。 package charactor;public class GiantDragon…