广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >ReentrantLock核心原理是什么
  • 384
分享到

ReentrantLock核心原理是什么

2024-04-02 19:04:59 384人浏览 薄情痞子
摘要

本篇内容介绍了“ReentrantLock核心原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!&n

本篇内容介绍了“ReentrantLock核心原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

 

前言

先来一个面试题

面试官:我看你熟悉ReentrantLock源码,能讲讲他的中断是怎么实现的么?

不知道也没关系,看完这篇文章通过你的思考,能找到答案哦

那我们开始吧

ReentrantLock 中文我们叫做可重入互斥锁,可重入的意思是同一个线程可以对同一个共享资源重复的加锁或释放锁,互斥就是 AQS  中的排它锁的意思,只允许一个线程获得锁。

简单应用

ReentrantLock 的使用相比较 synchronized 会稍微繁琐一点,所谓显示锁,也就是你在代码中需要主动的去进行 lock  操作。一般来讲我们可以按照下面的方式使用 ReentrantLock

Lock lock = new ReentrantLock(); lock.lock(); try {   doSomething(); }finally {   lock.unlock(); }

lock.lock () 就是在显式的上锁。上锁后,下面的代码块一定要放到 try 中,并且要结合 finally 代码块调用lock.unlock  ()来释放锁,否则一定 doSomething 方法中出现任何异常,这个锁将永远不会被释放掉。

公平锁和非公平锁

synchronized  是非公平锁,也就是说每当锁匙放的时候,所有等待锁的线程并不会按照排队顺去依次获得锁,而是会再次去争抢锁。ReentrantLock  相比较而言更为灵活,它能够支持公平和非公平锁两种形式。只需要在声明的时候传入 true。

Lock lock = new ReentrantLock(true);

而默认的无参构造方法则会创建非公平锁。

tryLock方法

前面我们通过 lock.lock (); 来完成加锁,此时加锁操作是阻塞的,直到获取锁才会继续向下进行。ReentrantLock  其实还有更为灵活的枷锁方式 tryLock。

tryLock 方法有两个重载,第一个是无参数的 tryLock 方法,被调用后,该方法会立即返回获取锁的情况。获取为 true,未能获取为  false。我们的代码中可以通过返回的结果进行进一步的处理。第二个是有参数的 tryLock  方法,通过传入时间和单位,来控制等待获取锁的时长。如果超过时间未能获取锁则放回 false,反之返回 true。使用方法如下:

if(lock.tryLock(2, TimeUnit.SECONDS)){    try {       doSomething();    } catch (InterruptedException e) {       e.printStackTrace();    }finally {       lock.unlock();    } }else{   doSomethingElse(); }

我们如果不希望无法获取锁时一直等待,而是希望能够去做一些其它事情时,可以选择此方式。

类结构

ReentrantLock 类本身是不继承 AQS 的,实现了 Lock 接口,如下:

public class ReentrantLock implements Lock, java.io.Serializable {}

Lock 接口定义了各种加锁,释放锁的方法,接口有如下几个:

// 获得锁方法,获取不到锁的线程会到同步队列中阻塞排队 void lock(); // 获取可中断的锁 void lockInterruptibly() throws InterruptedException; // 尝试获得锁,如果锁空闲,立马返回 true,否则返回 false boolean tryLock(); // 带有超时等待时间的锁,如果超时时间到了,仍然没有获得锁,返回 false boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 释放锁 void unlock(); // 得到新的 Condition Condition newCondition();

ReentrantLock 就负责实现这些接口,我们使用时,直接面对的也是这些方法,这些方法的底层实现都是交给 Sync 内部类去实现的,Sync  类的定义如下:

abstract static class Sync extends AbstractQueuedSynchronizer {}

都是最终继承自 AbstractQueuedSynchronizer。这就是著名的 AQS。通过查看 AQS 的注释我们了解到, AQS  依赖先进先出队列实现了阻塞锁和相关的同步器(信号量、事件等)。

AQS 内部有一个 volatile 类型的 state 属性,实际上多线程对锁的竞争体现在对 state 值写入的竞争。一旦 state 从 0 变为  1,代表有线程已经竞争到锁,那么其它线程则进入等待队列。

等待队列是一个链表结构的 FIFO 队列,这能够确保公平锁的实现。同一线程多次获取锁时,如果之前该线程已经持有锁,那么对 state 再次加  1。释放锁时,则会对 state-1。直到减为 0,才意味着此线程真正释放了锁。

ReentrantLock核心原理是什么

Sync 继承了 AbstractQueuedSynchronizer ,所以 Sync 就具有了锁的框架,根据 AQS 的框架,Sync 只需要实现  AQS 预留的几个方法即可,但 Sync 也只是实现了部分方法,还有一些交给子类 NonfairSync 和 FairSync 去实现了,NonfairSync  是非公平锁,FairSync 是公平锁,定义如下:

// 同步器 Sync 的两个子类锁 static final class FairSync extends Sync {} static final class NonfairSync extends Sync {}

几个类整体的结构如下:

ReentrantLock核心原理是什么

图中 Sync、NonfairSync、FairSync 都是静态内部类的方式实现的,这个也符合 AQS 框架定义的实现标准。

构造器

ReentrantLock 构造器有两种,代码如下:

// 无参数构造器,相当于 ReentrantLock(false),默认是非公平的 public ReentrantLock() {     sync = new NonfairSync(); }   public ReentrantLock(boolean fair) {     sync = fair ? new FairSync() : new NonfairSync(); }

无参构造器默认构造是非公平的锁,有参构造器可以选择。

从构造器中可以看出,公平锁是依靠 FairSync 实现的,非公平锁是依靠 NonfairSync 实现的

源码解析

Sync同步器

Sync 表示同步器,继承了 AQS:

ReentrantLock核心原理是什么

从图中可以看出,lock 方法是个抽象方法,留给 FairSync 和 NonfairSync 两个子类去实现。

加锁方法

FairSync公平锁

FairSync 公平锁只实现了 lock 和 tryAcquire 两个方法,lock 方法非常简单,如下:

// acquire 是 AQS 的方法,表示先尝试获得锁,失败之后进入同步队列阻塞等待 final void lock() {     acquire(1); }

在 FairSync 并没有重写 acquire 方法代码。调用的为 AbstractQueuedSynchronizer 的代码,如下:

public final void acquire(int arg) {     if (!tryAcquire(arg) &&         acquireQueued(addWaiter(node.EXCLUSIVE), arg))         selfInterrupt(); }

首先调用一次 tryAcquire 方法。如果 tryAcquire 方法返回 true,那么 acquire 就会立即返回。

但如果 tryAcquire 返回了 false,那么则会先调用 addWaiter,把当前线程包装成一个等待的 node,加入到等待队列。然后调用  acquireQueued 尝试排队获取锁,如果成功后发现自己被中断过,那么返回 true,导致 selfInterrupt  被触发,这个方里只是调用Thread.currentThread ().interrupt ();进行 interrupt。

acquireQueued 代码如下:

ReentrantLock核心原理是什么

在此方法中进入自旋,不断查看自己排队的情况。如果轮到自己( header 是已经获取锁的线程,而 header  后面的线程是排队到要去获取锁的线程),那么调用 tryAcquire 方法去获取锁,然后把自己设置为队列的  header。在自旋中,如果没有排队到自己,还会检查是否应该应该被中断。

整个获取锁的过程我们可以总结下:

直接通过 tryAcquire 尝试获取锁,成功直接返回;

如果没能获取成功,那么把自己加入等待队列;

自旋查看自己的排队情况;

如果排队轮到自己,那么尝试通过 tryAcquire 获取锁;

如果没轮到自己,那么回到第三步查看自己的排队情况。

从以上过程我们可以看到锁的获取是通过 tryAcquire 方法。而这个方法在 FairSync 和 NonfairSync 有不同实现。

这个tryAcquire 方法是 AQS 在 acquire 方法中留给子类实现的抽象方法,FairSync 中实现的源码如下:

ReentrantLock核心原理是什么

实际上它的实现和 NonfairSync 的实现,只是在 c==0 时,多了对 hasQueuedPredecessors  方法的调用。故名思义,这个方法做的事情就是判断当前线程是否前面还有排队的线程。

当它前面没有排队线程,说明已经排队到自己了,这是才会通过 CAS 的的方式去改变 state 值为  1,如果成功,那么说明当前线程获取锁成功。接下来就是调用 setExclusiveOwnerThread 把自己设置成为锁的拥有者。else if  中逻辑则是在处理重入逻辑,如果当前线程就是锁的拥有者,那么会把 state 加 1 更新回去。

通过以上分析,我们可以看出 AbstractQueuedSynchronizer 提供 acquire 方法的模板逻辑,但其中真正对锁的获取方法  tryAcquire,是在不同子类中实现的,这是很好的设计思想。

NonfairSync非公平锁

NonfairSync 底层实现了 lock 和 tryAcquire 两个方法,如下:

ReentrantLock核心原理是什么

nonfairTryAcquire

ReentrantLock核心原理是什么

以上代码有三点需要注意:

通过判断 AQS 的 state 的状态来决定是否可以获得锁,0 表示锁是空闲的;

else if 的代码体现了可重入加锁,同一个线程对共享资源重入加锁,底层实现就是把 state + 1,并且可重入的次数是有限制的,为 Integer  的最大值;

这个方法是非公平的,所以只有非公平锁才会用到,公平锁是另外的实现。

无参的 tryLock 方法调用的就是此方法,tryLock 的方法源码如下:

public boolean tryLock() {     // 入参数是 1 表示尝试获得一次锁     return sync.nonfairTryAcquire(1); }

其底层的调用关系(只是简单表明调用关系,并不是完整分支图)如下:

ReentrantLock核心原理是什么

解锁方法

public void unlock() {     sync.release(1); }

和 lock 很像,实际调用的是 sync 实现类的 release 方法。和 lock 方法一样,这个 release 方法在  AbstractQueuedSynchronizer 中,

if (tryRelease(arg)) {     Node h = head;     if (h != null && h.waitStatus != 0)         unparkSuccessor(h);     return true; } return false;

这个方法中会先执行 tryRelease,它的实现也在 AbstractQueuedSynchronizer 的子类 Sync  中,如果释放锁成功,那么则会通过 unparkSuccessor 方法找到队列中第一个 waitStatus<0的线程进行唤醒。我们下面看一下  tryRelease 方法代码:

ReentrantLock核心原理是什么

tryRelease 方法是公平锁和非公平锁都公用的,在锁释放的时候,是没有公平和非公平的说法的。

从代码中可以看到,锁最终被释放的标椎是 state 的状态为 0,在重入加锁的情况下,需要重入解锁相应的次数后,才能最终把锁释放,比如线程 A 对共享资源  B 重入加锁 5 次,那么释放锁的话,也需要释放 5 次之后,才算真正的释放该共享资源了。

总结

本篇文章 ReentrantLock 的使用及其核心源代码,其实 Lock 相关的代码还有很多。我们可以尝试自己去阅读。

ReentrantLock 的设计思想是通过 FIFO 的队列保存等待锁的线程。通过 volatile 类型的 state  保存锁的持有数量,从而实现了锁的可重入性。而公平锁则是通过判断自己是否排队成功,来决定是否去争抢锁。

然后我们了解到AQS 搭建了整个锁架构,子类锁只需要根据场景,实现 AQS 对应的方法即可,不仅仅是 ReentrantLock 是这样,JUC  中的其它锁也都是这样,只要对 AQS 了如指掌,锁其实非常简单。

“ReentrantLock核心原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: ReentrantLock核心原理是什么

本文链接: https://www.lsjlt.com/news/74965.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ReentrantLock核心原理是什么
    本篇内容介绍了“ReentrantLock核心原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!&n...
    99+
    2022-10-19
  • Axios核心原理是什么
    这篇文章主要介绍“Axios核心原理是什么”,在日常操作中,相信很多人在Axios核心原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Axios核心原理是什么”的疑惑...
    99+
    2022-10-19
  • ReactHook核心原理是什么
    本篇内容主要讲解“ReactHook核心原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ReactHook核心原理是什么”吧!基本准备工作手写useSt...
    99+
    2022-10-19
  • Vue的核心原理是什么
    这篇文章主要介绍“Vue的核心原理是什么”,在日常操作中,相信很多人在Vue的核心原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue的核心原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
    99+
    2023-06-29
  • Zookeeper的核心原理是什么
    这篇文章主要介绍“Zookeeper的核心原理是什么”,在日常操作中,相信很多人在Zookeeper的核心原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Zookee...
    99+
    2022-10-19
  • React-Redux的核心原理是什么
    这篇文章主要介绍“React-Redux的核心原理是什么”,在日常操作中,相信很多人在React-Redux的核心原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Re...
    99+
    2022-10-18
  • OAM Kubernetes 实现核心原理是什么
    OAM Kubernetes 实现核心原理是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。OAM 因何而生我们知道,应用容器技术自诞生开始,就以 “彻底改变...
    99+
    2023-06-04
  • Flutter核心原则是什么
    这篇文章主要介绍“Flutter核心原则是什么”,在日常操作中,相信很多人在Flutter核心原则是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Flutter核心原则是什么”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-04
  • Spring Cloud原理及核心组件是什么
    本篇内容介绍了“Spring Cloud原理及核心组件是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!概述毫无疑问,Sprin...
    99+
    2023-07-05
  • Java synchronized偏向锁的核心原理是什么
    本篇内容主要讲解“Java synchronized偏向锁的核心原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java synchronized偏向锁的核心原理是什...
    99+
    2023-06-29
  • ZooKeeper核心原理及应用场景是什么
    这篇文章将为大家详细讲解有关ZooKeeper核心原理及应用场景是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。为什么会有ZooKeeper 我们知道要写一个分布式应用是非常困...
    99+
    2023-06-02
  • kubernetes控制器StatefulSet核心实现原理是什么
    本篇内容主要讲解“kubernetes控制器StatefulSet核心实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“kubernetes控制器St...
    99+
    2022-10-19
  • synchronized和ReentrantLock的基本原理是什么
    这篇文章主要介绍“synchronized和ReentrantLock的基本原理是什么”,在日常操作中,相信很多人在synchronized和ReentrantLock的基本原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作...
    99+
    2023-06-15
  • Java中synchronized轻量级锁的核心原理是什么
    这篇文章将为大家详细讲解有关Java中synchronized轻量级锁的核心原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1. 轻量级锁的原理引入轻量级锁的主要目的是在多线程竞争不激烈的情况下,...
    99+
    2023-06-29
  • .NET WebSocket核心原理是怎样的
    .NET WebSocket核心原理是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。我们先深入研究基本概念,以了解Web...
    99+
    2022-10-19
  • vuex核心是什么
    这篇文章主要介绍“vuex核心是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vuex核心是什么”文章能帮助大家解决问题。 vuex是专门帮助vue管理的一个...
    99+
    2022-10-19
  • hadoop核心是什么
    hadoop的核心是分布式文件系统hdfs和MapReduce,HDFS为海量的数据提供了存储,而MapReduce则为海量的数据提供了计算,Hadoop是一个由Apache基金会所开发的分布式系统基础架构,用户可以在不了解分布式底层细节的...
    99+
    2022-10-07
  • JavaScript手写Promise核心原理
    目录准备完善 resolve/rejectthen异步处理链式调用边界处理catch优化后完整代码准备 首先,promise 有三种状态:pending fulf...
    99+
    2022-11-13
  • 什么是JavaScript的核心
    本篇内容主要讲解“什么是JavaScript的核心”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是JavaScript的核心”吧! ...
    99+
    2022-10-19
  • matplotlib的核心是什么
    小编给大家分享一下matplotlib的核心是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!matplotlib使用numpy进行数组运算,并调用一系列其他的...
    99+
    2023-06-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作