4.13 ReentrantLock

news/2024/12/4 4:04:09/

相对于 synchronized 它具备如下特点

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量
    与 synchronized 一样,都支持可重入
	基本语法// 获取锁reentrantLock.lock();try{// 临界区} finally{// 释放锁reentrantLock.unlock();}

1、可重入

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

@Slf4j(topic = "c.TestReentrant")
public class TestReentrant {static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {method1();}public static void method1() {lock.lock();try {log.debug("execute method1");method2();} finally {lock.unlock();}}public static void method2() {lock.lock();try {log.debug("execute method2");method3();} finally {lock.unlock();}}public static void method3() {lock.lock();try {log.debug("execute method3");} finally {lock.unlock();}}
}

由结果可以证明:ReentrantLock 是可重入的
在这里插入图片描述

2、可打断

举例说明:

@Slf4j(topic = "c.TestInterrupt")
public class TestInterrupt {public static void main(String[] args) {test1();}private static void test1() {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");try {lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("等锁的过程中被打断");return;}try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("获得了锁");t1.start();try {sleep(1);t1.interrupt();log.debug("执行打断");} finally {lock.unlock();}}
}

由结果可以看到,main线程获取到了锁,t1线程尝试去获取锁,然后main线程打断t1线程尝试获取锁的过程
在这里插入图片描述

注意如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断

@Slf4j(topic = "c.TestInterrupt")
public class TestInterrupt {public static void main(String[] args) {test2();}private static void test2() {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");lock.lock();try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("获得了锁");t1.start();try {sleep(1);t1.interrupt();log.debug("执行打断");sleep(1);} finally {log.debug("释放了锁");lock.unlock();}}
}

可以看到t1被主线程打断后,并没异常抛出错误,而是正常执行
在这里插入图片描述

3、锁超时

1) 立即失败的场景

public class TestTimeout {public static void main(String[] args) {test2();}private static void test2() {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");if (!lock.tryLock()) {log.debug("获取立刻失败,返回");return;}try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("获得了锁");t1.start();try {sleep(2);} finally {lock.unlock();}}
}

main线程首先获取到了锁,t1线程尝试去获取锁的时候就会立即失败
在这里插入图片描述

2)超时失败

public class TestTimeout {public static void main(String[] args) {test1();}private static void test1() {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("启动...");try {if (!lock.tryLock(1, TimeUnit.SECONDS)) {log.debug("获取等待 1s 后失败,返回");return;}} catch (InterruptedException e) {e.printStackTrace();}try {log.debug("获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();log.debug("获得了锁");t1.start();try {sleep(2);} finally {lock.unlock();}}
}

由结果可以判断t1线程在尝试获取锁时,阻塞了一秒钟,一秒后还是没有获取到锁,则返回false
在这里插入图片描述

4、公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁,ReentrantLock 默认是不公平的

demo演示证明:ReentrantLock 默认是不公平的

public class TestFair {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock(false);lock.lock();for (int i = 0; i < 20; i++) {new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + " running...");} finally {lock.unlock();}}, "t" + i).start();}// 1s 之后去争抢锁Thread.sleep(1000);for (int i = 0; i < 5; i++) {new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + " running...");} finally {lock.unlock();}}, "强行插入").start();}lock.unlock();}
}

按道理说,500个线程是先启动的,应该先获取到锁,但是实际情况,强制插入线程中途就获取到锁了
在这里插入图片描述

5、条件变量

synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比

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

使用要点:

  • await 前需要获得锁
  • await 执行后,会释放锁,进入 conditionObject 等待
  • await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
  • 竞争 lock 锁成功后,从 await 后继续执行
@Slf4j(topic = "c.TestCondition")
public class TestCondition {static ReentrantLock lock = new ReentrantLock();static Condition waitCigaretteQueue = lock.newCondition();static Condition waitbreakfastQueue = lock.newCondition();static volatile boolean hasCigrette = false;static volatile boolean hasBreakfast = false;public static void main(String[] args) {new Thread(() -> {try {lock.lock();log.debug("准备工作,看看有没有烟: {}", hasCigrette);while (!hasCigrette) {log.debug("准备工作,看看有没有烟: {}", hasCigrette);try {// 释放锁,并在该条件变量上等待waitCigaretteQueue.await(); } catch (InterruptedException e) {e.printStackTrace();}}log.debug("等到了它的烟");} finally {lock.unlock();}}).start();new Thread(() -> {try {lock.lock();log.debug("准备工作,看看有没有早餐: {}", hasBreakfast);while (!hasBreakfast) {log.debug("准备工作,看看有没有早餐: {}", hasBreakfast);try {waitbreakfastQueue.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("送烟来了");hasCigrette = true;waitCigaretteQueue.signal();} finally {lock.unlock();}}private static void sendBreakfast() {lock.lock();try {log.debug("送早餐来了");hasBreakfast = true;waitbreakfastQueue.signal();} finally {lock.unlock();}}
}

在这里插入图片描述

6、同步模式之顺序控制


http://www.ppmy.cn/news/433129.html

相关文章

串口协议说明

文章目录 波特率概念波特率相对误差UART误差保证 协议常见的串行接口协议之间的比较USB 转串口PL2303USB 转串口CP2102USB转232 串口电平TTL电平485电平 帧奇偶校验 波特率 概念 波特率的概念请点我 波特率相对误差 波特率的相对误差要小于4%或者5%。不会影响数据的正常接收…

【cutlass】cuTe layout操作

简介 cuTe提供了对Layout操作的算法&#xff0c;可以混合执行来构建更复杂的Layout操作&#xff0c;比如在其他layout之间切分和平铺layout 在host或者device上打印cuTe cuTe的打印函数可以在host和device端打印。cute::print 重载了几乎所有 CuTe 类型&#xff0c;包括指针…

k8s 集群部署尝试

K8S 部署方式有很多&#xff0c;有的方式不太友好&#xff0c;需要注意很多关键点&#xff0c;有的方式对小白比较友好&#xff0c;部署简单方便且高效 二进制源码包的部署方式 使用 二进制源码包的方式部署会比较麻烦&#xff0c;大概分为如下几步&#xff1a; 获取源码包部…

深入了解模板知识(c++)

前言 在c中模板是很重的&#xff0c;泛型编程就是模板最好的体现&#xff0c;模板的出现就是为了更好的复用代码&#xff0c;有了它&#xff0c;我们不必写各种逻辑相同只是逻辑中的数据的类型的不同的代码&#xff0c;使得我们编写代码变得更加高效&#xff0c;下面让我们一起…

哪个星座的心肠最硬?

了解查询星座运势&#xff0c;关注“去问”就可以了。善良是一种优秀的品质&#xff0c;但有时候太过善良反而会成为被人利用的软肋&#xff0c;此时就需要学着让自己的心肠变硬&#xff0c;并且变得更加坚毅才不会被人欺负。那么&#xff0c;在十二星座中&#xff0c;有哪些星…

涌html编写星空图,canvas实现十二星座星空图

效果如下&#xff1a; 代码如下&#xff1a;canvas星座 * { margin: 0; padding: 0; } #box{ margin:10px 0 0 10px;; } input{ outline: none; font-size:16px; } p{ margin-bottom: 10px } input[typedate]{ height:36px; text-indent:10px; } input[typebutton]{ background…

Rasa使用——以星座运势为case

经过两周的摸索和实验&#xff0c;今天终于把星座运势推进到最后的测试阶段。尽管跑通一个demo会觉得rasa非常简单&#xff0c;但完成一个function&#xff0c;从分离不同的intent&#xff0c;对话框架设计&#xff0c;设置rasa pipeline&#xff0c;具体月份的读取&#xff0c…

用python来抓取qq空间好友星座和年龄等信息

############## 绘制星座信息图表 获取性别信息、绘制图表 ######### import json import requests from lxml import etree import pandas as pd import pygal import pickle ################################################################# ################ part 1…