【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

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

spring IOC的理解之原理和实现过程

《springIOC的理解之原理和实现过程》:本文主要介绍springIOC的理解之原理和实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、IoC 核心概念二、核心原理1. 容器架构2. 核心组件3. 工作流程三、关键实现机制1. Bean生命周期2.

MySQL数据库约束深入详解

《MySQL数据库约束深入详解》:本文主要介绍MySQL数据库约束,在MySQL数据库中,约束是用来限制进入表中的数据类型的一种技术,通过使用约束,可以确保数据的准确性、完整性和可靠性,需要的朋友... 目录一、数据库约束的概念二、约束类型三、NOT NULL 非空约束四、DEFAULT 默认值约束五、UN

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例

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

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