【Interview】深入理解ReentrantLock锁

2024-05-13 07:58

本文主要是介绍【Interview】深入理解ReentrantLock锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

java知识归纳总结
github: https://a870439570.github.io/interview-docs

什么是ReentrantLock

  • ReentrantLock是一个可重入的互斥锁锁, 实现Lock接口。具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义。ReentrantLock是显示的获取或释放锁,并且有锁超时,锁中断等功能。

  • 内部维户了一个Sync的内部类,继承AQS队列同步器。

  • ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。

  • 默认是非公平锁的实现方式

非公平锁获取和释放流程

加锁

  • 执行lock方法加锁时调用内部NonfairSynclock方法,第一次会快速尝试获取锁,执行AQS类的compareAndSetState方法(CAS)更改同步状态成员变量state,如果获取成功 则设置当前线程为锁的持有者。失败则执行AQS类的acquire方法,acquire会调用的AQS中的tryAcquire方法。这个tryAcquire方法需要自定义同步组件提供实现。
  • tryAcquire的具体流程是执行Sync类的nonfairTryAcquire方法:首先记录当前加锁线程,然后调用getState获取同步状态,如果为0时 说明锁处于空闲状态,可以获取,会以CAS方式修改state变量。成功则设置当前线程 返回true。否则执行重入判断,判断当前访问线程和已经持有锁的线程是否是同一个。如果相同,将同步状态值进行增加,并返回true。否则返回加锁失败false

解锁

  • 解锁unlock方法会调用内部类SynctryRelease方法。tryRelease首先调用getState方法获取同步状态,并进行了减法操作。在判断释放操作是不是当前线程,不是则抛出异常,然后判断同步状态是否等于0,如果是0,说明没有线程持有,锁是空闲的,则将当前锁的持有者设置为null, 方便其它线程获取,并返回true。否则返回false

ReentrantLock常用方法介绍

  • getHoldCount() 查询当前线程保持此锁的次数。
  • getOwner() 返回目前拥有此锁的线程
  • getQueueLength() 返回正等待获取此锁的线程估计数
  • getWaitingThreads(Condition condition) 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
  • boolean hasQueuedThread(Thread thread) 查询给定线程是否正在等待获取此锁。
  • boolean hasQueuedThreads() 查询是否有些线程正在等待获取此锁。
  • boolean hasWaiters(Condition condition) 查询是否有些线程正在等待与此锁有关的给定条件
  • boolean isHeldByCurrentThread() 查询当前线程是否保持此锁。
  • void lock() 获取锁。
  • void lockInterruptibly() 如果当前线程未被中断,则获取锁。
  • Condition newCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例。
  • boolean tryLock() 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
  • void unlock() 释放锁

程序中使用

  private  ReentrantLock lock=new ReentrantLock();private  int i=0;public  void  a(){lock.lock();i++;lock.unlock();}

ReentrantLock源码分析

加锁操作的执行流程

  • 1.当获得一个非公平锁时,也就是执行 new ReentrantLock();方法时会返回一个非公平的锁对象。源码中是如下的实现方式。
    public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
  • NonfairSync是一个内部类 是Sync的子类。Sync也是一个内部类,继承AQS,同步状态的获取或释放都是基于AQS来实现的
  • 执行lock方法时会调用如下NonfairSync的lock方法
 final void lock() {//第一次快速获取锁,使用CAS 方式 成功设置当前线程为锁的持有者if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else//锁获取失败时,调用AQS的acquire去获取锁,//acquire会调用tryAcquire方法,tryAcquire需要自定义同步组件提供实现,//所以这里的调用逻辑是acquire-》tryAcquire(NonfairSync类的)-》Sync的nonfairTryAcquire方法acquire(1);}//调用父类nonfairTryAcquire 实现加锁protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
  • 首先第一次尝试获取锁,以CAS方式设置state状态变量,成功设置当前线程为锁的持有者。失败就执行AQSacquire方法
  • acquire方法会执行tryAcquire方法,tryAcquire需要自定义同步组件提供具体实现,所以这里就会执行下面的tryAcquire方法
  • nonfairTryAcquire方法就是内部类Sync的自定义同步组件提供的具体实现,锁的获取逻辑就是考这个实现的。
    源码如下
     /*** 执行非公平的t加锁*/final boolean nonfairTryAcquire(int acquires) {//记录当前加锁线程final Thread current = Thread.currentThread();//获取同步状态 AQS中的volatile修饰的int类型成员变量 state  int c = getState();//为0时 说明锁处于空闲状态,可以获取if (c == 0) {// CAS方式修改state。成功则设置当前线程 返回trueif (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//线程重入判断,判断当前访问线程和已经持有锁的线程是否是同一个else if (current == getExclusiveOwnerThread()) {//将同步状态值进行增加if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置同步状态,重入锁的话就累加,并返回truesetState(nextc);return true;}return false;}
  • 首先先记录当前执行获取锁操作的线程。然后获获取同步状态state的值·
  • 如果state为0说明锁处于空闲状态,可以直接获取。会以CAS方式设置同步状态,成功则设置当前线程为锁的持有者,并返回true
  • compareAndSetState这个方法是执行unsafe类的compareAndSwapInt(Object var1, long var2, int var4, int var5)方法,其中第一个参数为需要改变的对象,第二个为偏移量(即之前求出来的valueOffset的值),第三个参数为期待的值,第四个为更新后的值。
  • 如果state不等于0.说明线程已经被持有。这里就要判断是否是重入锁(判断当前线程是否是锁的持有者),如果是重入就要累加state状态的值,并返回true。不是就直接返回false,加锁失败进入同步等待队列

套用网上的一个图

ReentrantLock 源码

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
public class ReentrantLock implements Lock, java.io.Serializable {private static final long serialVersionUID = 7373984872572414699L;private final Sync sync;/**内部维护的一个帮助类,继承成AQS  锁的获取和释放主要靠它**/abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;abstract void lock();/*** 执行非公平的t加锁*/final boolean nonfairTryAcquire(int acquires) {//记录当前加锁线程final Thread current = Thread.currentThread();//获取同步状态 AQS中的volatile修饰的int类型成员变量 state  int c = getState();//为0时 说明锁处于空闲状态,可以获取if (c == 0) {// CAS方式修改state。成功则设置当前线程 返回trueif (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//线程重入判断,判断当前访问线程和已经持有锁的线程是否是同一个else if (current == getExclusiveOwnerThread()) {//将同步状态值进行增加if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置同步状态,重入锁的话就累加,并返回truesetState(nextc);return true;}return false;}//释放锁,就是把AQS中的同步状态变量就行类减直到0 就是出于空闲状态了protected final boolean tryRelease(int releases) {int c = getState() - releases;//如果释放操作不是当前线程 则抛出异常if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;//同步状态等于0,说明没有线程持有,锁是空闲的if (c == 0) {free = true;//当前锁的持有者 设置为null 方便其它线程获取setExclusiveOwnerThread(null);}//如果该锁被获取了n次,那么前(n-1)次tryRelease(int releases)方法必须返回falsesetState(c);return free;}protected final boolean isHeldExclusively() {return getExclusiveOwnerThread() == Thread.currentThread();}final ConditionObject newCondition() {return new ConditionObject();}//返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。final Thread getOwner() {return getState() == 0 ? null : getExclusiveOwnerThread();}//查询当前线程保持此锁的次数。final int getHoldCount() {return isHeldExclusively() ? getState() : 0;//查询锁是否被持有final boolean isLocked() {return getState() != 0;}private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();setState(0); // reset to unlocked state}}//非公平的  Sync的子类static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;final void lock() {//第一次快速获取锁,使用CAS 方式 成功设置当前线程为锁的持有者if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else//锁获取失败时,调用AQS的acquire去获取锁,//acquire会调用tryAcquire方法,tryAcquire需要自定义同步组件提供实现,//所以这里的调用逻辑是acquire-》tryAcquire(NonfairSync类的)-》Sync的nonfairTryAcquire方法acquire(1);}//调用父类nonfairTryAcquire 实现加锁protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}//公平的 Sync的子类static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;// 加锁 调用AQS中的acquire方法,acquire会调用下面的tryAcquire方法final void lock() {acquire(1);}//加锁的过程,和父类的调用父类nonfairTryAcquire方法大致一样//唯一不同的位置为判断条件多了hasQueuedPredecessors()方法protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {//公平锁实现的关键点hasQueuedPredecessors/**即加入了同步队列中当前节点是否有前驱节点的判断如果该方法返回true,则表示有线程比当前线程更早地请求获取锁因此需要等待前驱线程获取并释放锁之后才能继续获取锁*/if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}}//AQS中的方法 判断当前线程是否位于CLH同步队列中的第一个。如果是则返回true,否则返回false。public final boolean hasQueuedPredecessors() {Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}//默认的构造函数  非公平锁public ReentrantLock() { sync = new NonfairSync();}//为true 公平锁public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}public void lock() { sync.lock();}public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}public boolean tryLock() { return sync.nonfairTryAcquire(1);}public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}//释放锁public void unlock() {  sync.release(1);}public Condition newCondition() {return sync.newCondition();}public int getHoldCount() { return sync.getHoldCount(); }public boolean isHeldByCurrentThread() { return sync.isHeldExclusively();  }public boolean isLocked() {return sync.isLocked(); }public final boolean isFair() {   return sync instanceof FairSync;  }protected Thread getOwner() {return sync.getOwner(); }public final boolean hasQueuedThreads() {return sync.hasQueuedThreads();}public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread);}public final int getQueueLength() { return sync.getQueueLength(); }protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); }public boolean hasWaiters(Condition condition) {if (condition == null) throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);}public int getWaitQueueLength(Condition condition) {if (condition == null) throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);}protected Collection<Thread> getWaitingThreads(Condition condition) {if (condition == null) throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);}}
  • 参考并发编程的艺术
  • jdk8源码
  • Github: https://github.com/a870439570

这篇关于【Interview】深入理解ReentrantLock锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985119

相关文章

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言