线程安全(七)ReentrantLock 简介、Condition 条件变量、锁的工作原理、synchronized 与 Lock 的区别

news2024/12/24 17:47:23

目录

    • 一、ReentrantLock 简介
      • 1.1 Reentrant 的特性:
      • 1.2 基本语法
      • 1.3 ReentrantLock 的主要方法:
      • 1.4 lock()、tryLock()、lockInterruptibly() 的区别:
    • 二、Condition 条件变量
      • 2.1 什么是 Condition 条件变量?
      • 2.2 Condition 的核心方法:
      • 2.3 Condition 使用示例1:等待与唤醒
      • 2.4 Condition 使用示例2:生产者-消费者模式
    • 三、ReentrantLock 的工作原理
    • 四、synchronized 和 Lock 的区别:
    • 五、使用 ReentrantLock 的注意事项

一、ReentrantLock 简介

  • ReentrantLock,也被称为 “可重入锁”,是一个同步工具类,在 java.util.concurrent.locks 包下。这种锁的一个重要特点是,它允许一个线程多次获取同一个锁而不会产生死锁。这与 synchronized 关键字提供的锁定机制非常相似,但 ReentrantLock 提供了更高的扩展性

1.1 Reentrant 的特性:

  1. 可中断: lockInterruptibly()方法获取的锁可以被中断。这提供了另一个相对于 synchronized 关键字的优势,因为 synchronized 不支持响应中断。
  2. 可以设置超时时间: tryLock(long timeout, TimeUnit unit) 方法提供了获取锁超时的功能。它的语义是 在指定的时间内如果获取到锁就返回 true,获取不到则返回 false。这种机制避免了线程无期限地等待锁释放。
  3. 可以设置公平锁: ReentrantLock 在初始化时提供了一个公平锁选项。公平锁会按照线程请求锁的顺序来分配锁,而不是像非公平锁那样允许线程抢占已经等待的线程的锁。公平锁可以减少 “饥饿” 的情况,但也可能降低一些性能。
  4. 支持多个条件变量: ReentrantLock 类中还包含一个 Condition 接口的实现,该接口允许线程在某些条件下等待(await()方法)或唤醒(signal()signalAll()方法)。这提供了一种比使用 wait() 和 notify() 更灵活和更安全的线程通信方式。
  5. 支持重入: ReentrantLock 的一个主要特点是它的名字所表达的含义——“可重入”。简单来说,如果一个线程已经持有了某个锁,那么它可以再次调用 lock() 方法而不会被阻塞。 这在某些需要递归锁定的场景中非常有用。锁的持有计数(state变量)会在每次成功调用 lock() 方法时递归,并在每次 unlock() 方法被调用时递减。

1.2 基本语法

ReentrantLock 的基本语法如下:

// 创建锁对象
private ReentrantLock lock = new ReentrantLock();
try {
   
    // 获取锁
    lock.lock();
} finally {
   
    // 释放锁
    lock.unlock();
}

1.3 ReentrantLock 的主要方法:

  1. void lock():执行此方法时,如果锁处于空闲状态,当前线程将获取到锁。相反,如果锁已经被其他线程持有,将禁用当前线程,直到当前线程获取到锁。
  2. boolean tryLock()如果锁可用,则获取锁,并立即返回 true,否则返回 false。该方法和 lock() 的区别在于,tryLock() 只是 “试图” 获取锁,如果锁不可用,不会导致当前线程被禁用,当前线程仍然继续往下执行代码。而 lock() 方法则是一定要获取到锁,如果锁不可用,就一直等待,在未获得锁之前,当前线程并不继续向下执行。
  3. boolean tryLock(long time, TimeUnit unit)如果锁在给定时间内没有被另一个线程保持,则获取该锁。
  4. void unlock():执行此方法时,当前线程将释放持有的锁。锁只能由持有者释放,如果线程并不持有锁,却执行该方法,可能导致异常的发生。
  5. Condition newCondition()条件对象,获取等待通知组件。该组件和当前的锁绑定,当前线程只有获取了锁,才能调用该组件的 await() 方法,而调用后,当前线程将释放锁。
  6. getHoldCount()查询当前线程保持此锁的次数,也就是执行此线程执行 lock 方法的次数。
  7. getQueueLength()返回正等待获取此锁的线程估计数,比如启动 10 个线程,1个线程获得锁,此时返回的是9。
  8. getWaitQueueLength(Condition condition)返回等待与此锁相关的给定条件的线程估计数。比如 10 个线程,用同一个 condition 对象,并且此时这 10 个线程都执行了 condition 对象的 await 方法,那么此时执行此方法返回 10。
  9. hasWaiters(Condition condition)查询是否有线程等待此锁有关的给定条件(condition)。即对于指定 condition 对象,查询有多少线程执行了 condition.await() 方法。
  10. hasQueueThread(Thread thread)查询给定线程是否等待获取此锁
  11. hasQueueThreads()是否有线程等待此锁
  12. isFair()该锁是否为公平锁
  13. isHeldByCurrentThread()当前线程是否保持锁锁定,线程的执行 lock() 方法的前后分别是 false 和 true。
  14. isLock()此锁是否有任意线程占用
  15. lockInterruptibly()如果当前线程未被中断,获取锁

1.4 lock()、tryLock()、lockInterruptibly() 的区别:

  1. lock() 能获得锁就返回 true,不能的话会一直等待获取锁。

  2. tryLock() 能获得所就返回 true,不能立即返回 false,不会一直等待。

    tryLock(long timeout, TimeUnit unit) 可以增加时间限制,如果超过该时间段还没获得锁,返回 false。

  3. lock()lockInterruptibly(),如果两个线程分别执行这两个方法,但如果此时中断这两个线程,lock() 不会抛出异常,而 lockInterruptibly() 会抛出异常。


二、Condition 条件变量

2.1 什么是 Condition 条件变量?

  • Condition 条件变量是 ReentrantLock 的一部分,它提供了比 Object 监视器方法(如 wait()notify())更高级的线程等待/唤醒机制。Condition 允许更精确地控制线程间的同步,特别是当需要基于特定条件进行等待时。

Condition 接口通常与 ReentrantLock 一起使用,以实现更复杂的线程间协作模式,如:生产者-消费者模式、读者-写者模式等。

2.2 Condition 的核心方法:

下面介绍 3 个 Condition 的方法:

  • await():方法会使当前线程进入等待状态,并释放 ReentrantLock 锁,直到其他线程调用了相应的 signal()signalAll() 方法时才会被唤醒。

  • signal():方法用于唤醒一个等待在该 Condition 上的线程。

    (注意:signal() 不会立即唤醒线程,而是将其放入就绪队列,由操作系统调度器决定何时真正唤醒。)

  • signalAll():方法会唤醒所有等待在该 Condition 上的线程。通常在需要所有等待线程都参与接下来的执行流程时使用。

2.3 Condition 使用示例1:等待与唤醒

下面这个示例展示了如何使用ReentLock和Condition实现线程间通信,通过控制锁的获取和释放以及条件变量的等待和唤醒,可以实现更灵活的线程同步和通信:

ReentrantLockExample.java

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
   
    /**
     * 初始化锁
     */
    private Lock lock = new ReentrantLock();
    // Lock lock = new ReentrantLock(true); // 公平锁
    // Lock lock = new ReentrantLock(false); // 非公平锁

    /**
     * 创建 Condition
     */
    private Condition condition = lock.newCondition(); // 创建 Condition

    /**
     * wait 方法等待。
     */
    private void testConditionWait

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

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

相关文章

PJA1介导的焦亡抑制是鼻咽癌产生耐药性的驱动因素

引用信息 文 章:PJA1-mediated suppression of pyroptosis as a driver of docetaxel resistance in nasopharyngeal carcinoma. 期 刊:Nature Communications(影响因子:14.7) 发表时间:2024年6月2…

LLaMA-Factory

文章目录 一、关于 LLaMA-Factory项目特色性能指标 二、如何使用1、安装 LLaMA Factory2、数据准备3、快速开始4、LLaMA Board 可视化微调5、构建 DockerCUDA 用户:昇腾 NPU 用户:不使用 Docker Compose 构建CUDA 用户:昇腾 NPU 用户&#xf…

变阻器与电位器有什么区别?

变阻器和电位器都是可以改变电阻值的电子元件,它们在电路中的作用和调节方式有一定的相似性,但它们之间还是存在一些区别的。 1. 结构上的区别:变阻器主要由固定电阻体和可动滑片组成,通过滑动滑片来改变电阻体的电阻值。而电位器…

数据库(创建数据库和表)

目录 一:创建数据库 二:创建表 2.1:创建employees表 2.2:创建orders表 2.3:创建invoices表 一:创建数据库 mysql> create database mydb6_product; Query OK, 1 row affected (0.01 sec) mysql&g…

linux centos limits.conf 修改错误,无法登陆问题修复 centos7.9

一、问题描述 由于修改/etc/security/limits.conf这个文件中的值不当,重启后会导致其账户无法远程登录,本机登录。 如改成这样《错误示范》: 会出现: 二、解决 现在知道是由于修改limits.conf文件不当造成的,那么就…

智慧农业新纪元:解锁新质生产力,加速产业数字化转型

粮食安全乃国家之根本,“浙江作为农业强省、粮食生产重要省份,在维护国家粮食安全大局中肩负着重大使命。浙江粮食产业经济年总产值已突破4800亿元,稳居全国前列,然而,同样面临着规模大而不强、质量效益有待提升、数字…

JVM高频面试点

文章目录 JVM内存模型程序计数器Java虚拟机栈本地方法栈Java堆方法区运行时常量池 Java对象对象的创建如何为对象分配内存 对象的内存布局对象头实例数据对齐填充 对象的访问定位 垃圾收集器找到垃圾引用计数法可达性分析(根搜索法) 引用概念的扩充回收方…

字符数组的魅力:C语言字符数组与字符串编程实践

1.概念 字符数组,数组元素是char(字符型)的数组,它可以是一维数组,也可以是二维数组。 2.定义的时候赋值 char ch1[]{c,h,i,n,a}; char ch2[]{"china"}; //相当于 char ch2[] "china"; 元素个数为6,默认会…

探索Linux世界 —— shell与权限的相关知识

一、shell以及其运行原理 1、什么是shell Linux严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用kernel。而是通过kernel的“外壳”程序,也就是所谓的shell&#x…

6个高效再利用的UI作品集设计模板

UI 作品集是指用户界面设计师的个人作品集。它展示了设计师的设计能力、技巧和风格,也是充分展示他们设计能力的证明。优秀的UI 作品集应具有简洁明了、美观大方、良好的互动体验和明确的目标。本文将从两个方面的介绍 Ui 作品集模板的全部内容:UI 作品集…

Linux - 基础开发工具(yum、vim、gcc、g++、make/Makefile、git、gdb)

目录 Linux软件包管理器 - yum Linux下安装软件的方式 认识yum 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式各命令汇总 vim底行模式各命令汇总 vim的简单配置 Linux编译器 - gc…

近距离无线通信技术简介

个人早几年整理的材料,学识有限,喜欢指正。

Java.Net.UnknownHostException:揭开网络迷雾,解锁异常处理秘籍

在Java编程的浩瀚宇宙中,java.net.UnknownHostException犹如一朵不时飘过的乌云,让开发者在追求网络畅通无阻的道路上遭遇小挫。但别担心,今天我们就来一场说走就走的探险,揭秘这个异常的真面目,并手把手教你几招应对之…

【React】React18 Hooks 之memo、useCallback

目录 React.memo()案例1: 无依赖项,无props案例1: props比较机机制(1)传递基本类型,props变化时组件重新渲染(2)传递的是引用类型的prop,比较的是新值和旧值的引用(3)保证…

pyinstall 打包基于PyQt5和PaddleOCR的项目为.exe

简介: 最近做了一个小项目,是基于PyQt5和PaddleOCR的。需要将其打包为.exe,然后打包过程中遇到了很多问题,也看了很多教程,方法千奇百怪的,最后也是一步一步给试出来了。记录一下,防止以后忘记…

【2024年“数据要素×”大赛宁夏分赛】赛程制度

“数据要素”大赛宁夏分赛怎么报名?看这里 官方网站:https://nxsjysds.sznxgs.com/ 赛程制度 (一)启动报名(7月16日-8月10日)举办启动仪式,组织线上和线下推广工作,动员参赛队伍报…

vue3 vxe-grid修改currentPage,查询数据的时候,从第一页开始查询

1、当我们设置好VxeGrid.Options进行数据查询的时候,下面是可能的设置&#xff1a; const gridOptions reactive<BasicTableProps>({id: UserTable,showHeaderOverflow: false,showOverflow: true,keepSource: true,columns: userColumns,size: small,pagerConfig: {cur…

Python求均值,方差,标准差

参考链接&#xff1a;变异系数&#xff08;Coefficient of Variation,COV&#xff09;和协方差&#xff08;Covariance, Cov&#xff09;-CSDN博客 参考链接&#xff1a;pandas中std和numpy的np.std区别_numpy pandas std-CSDN博客 在计算蛋白质谱数据中的每个蛋白对应的变异…

【Java面向对象】多态

文章目录 1.动态绑定2.对象转换和 instanceof 操作符稍作总结3.Object 类的 equals 方法4.ArrayList 类5.继承体系中的权限问题6.final 1.动态绑定 多态意味着父类型的变量可以引用子类型的对象。 方法可以在沿着继承链的多个类中实现。JVM 决定运行时调用哪个方法。 一个变量…

新手小白攻略:如何用AI工具搭建个人知识库

个人知识库是指个人通过积累和整理&#xff0c;将各种领域的知识、经验和技能进行分类、归纳和存储的系统化工具或平台。 随着信息技术的飞速发展和知识经济的兴起&#xff0c;个人知识库不仅成为个人学习、成长和创新的基石&#xff0c;也是适应快速变化社会、提升竞争力的关…