返回顶部
首页 > 资讯 > 前端开发 > node.js >ReentrantLock是怎么基于AQS实现的
  • 615
分享到

ReentrantLock是怎么基于AQS实现的

2024-04-02 19:04:59 615人浏览 八月长安
摘要

这篇文章主要介绍了ReentrantLock是怎么基于AQS实现的的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇ReentrantLock是怎么基于AQS实现的文章都会有所收获

这篇文章主要介绍了ReentrantLock是怎么基于AQS实现的的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇ReentrantLock是怎么基于AQS实现的文章都会有所收获,下面我们一起来看看吧。

  ReentrantLock是一个可重入的互斥,基于AQS实现,它具有与使用 synchronized 方法和语句相同的一些基本行为和语义,但功能更强大。

  lock和unlock

  ReentrantLock 中进行同步操作都是从lock方法开始。lock获取锁,进行一系列的业务操作,结束后使用unlock释放锁。

  private final ReentrantLock lock = new ReentrantLock();

  public void sync(){

  lock.lock();

  try {

  // … method body

  } finally {

  lock.unlock()

  }

  }

  lock

  ReentrantLock 中lock的实现是通过调用AQS的 AbstractQueuedSynchronizer#acquire 方法实现。

  public final void acquire(int arg) {

  //尝试获取锁

  if (!tryAcquire(arg) &&

  acquireQueued(addWaiter(node.EXCLUSIVE), arg))

  selfInterrupt();

  }

  根据之前介绍的模板方法模式,对于锁的获取tryAcquire是在ReentrantLock中实现的。而非公平锁中的实际实现方法为nonfairTryAcquire。

  ReentrantLock#nonfairTryAcquire

  protected final boolean tryAcquire(int acquires) {

  return nonfairTryAcquire(acquires);

  }

  final boolean nonfairTryAcquire(int acquires) {

  final Thread current = Thread.currentThread();

  int c = getState();

  if (c == 0) {

  if (compareAndSetState(0, acquires)) {

  setExclusiveOwnerThread(current);

  return true;

  }

  }

  else if (current == getExclusiveOwnerThread()) {

  int nextc = c + acquires;

  if (nextc < 0) // overflow

  throw new Error("Maximum lock count exceeded");

  setState(nextc);

  return true;

  }

  return false;

  }

  在获取锁的逻辑中首先是尝试以cas方式获取锁,如果获取失败则表示锁已经被线程持有。

  再判断持有该锁的线程是否为当前线程,如果是当前线程就将state的值加1,在释放锁是也需要释放多次。这就是可重入锁的实现。

  如果持有锁的线程并非当前线程则这次加锁失败,返回false。加锁失败后将调用 AbstractQueuedSynchronizer#acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 。

  首先会调用addWaiter方法将该线程入队。

  private Node addWaiter(Node mode) {

  Node node = new Node(Thread.currentThread(), mode);

  Node pred = tail;

  if (pred != null) {

  node.prev = pred;

  if (compareAndSetTail(pred, node)) {

  pred.next = node;

  return node;

  }

  }

  enq(node);

  return node;

  }

  mode是指以何种模式的节点入队,这里传入的是Node.EXCLUSIVE(独占锁)。首先将当前线程包装为node节点。然后判断等待队列的尾节点是否为空,如果不为空则通过cas的方式将当前节点接在队尾。如果tail为空则执行enq方法。

  AbstractQueuedSynchronizer#enq

  private Node enq(final Node node) {

  for (;;) {

  Node t = tail;

  if (t == null) { // Must initialize

  if (compareAndSetHead(new Node()))

  tail = head;

  } else {

  node.prev = t;

  if (compareAndSetTail(t, node)) {

  t.next = node;

  return t;

  }

  }

  }

  }

  enq方法通过for(;;)无限循环的方式将node节点设置到等待队列的队尾(队列为空时head和tail都指向当前节点)。

  综上可知addWaiter方法的作用是将竞争锁失败的节点放到等待队列的队尾。

  等待队列中的节点也并不是什么都不做,这些节点也会不断的尝试获取锁,逻辑在acquireQueued中实现。

  AbstractQueuedSynchronizer#acquireQueued

  final boolean acquireQueued(final Node node, int arg) {

  boolean failed = true;

  try {

  boolean interrupted = false;

  for (;;) {

  final Node p = node.predecessor();

  if (p == head && tryAcquire(arg)) {

  setHead(node);

  p.next = null; // help GC

  failed = false;

  return interrupted;

  }

  if (shouldParkAfterFailedAcquire(p, node) &&

  parkAndCheckInterrupt())

  interrupted = true;

  }

  } finally {

  if (failed)

  cancelAcquire(node);

  }

  }

  可以看到该方法也是使用for(;;)无限循环的方式来尝试获取锁。首先判断当前节点是否为头结点的下一个节点,如果是则再次调用tryAcquire尝试获取锁。当然这个过程并不是一定不停进行的,这样的话多线程竞争下cpu切换也极耗费资源。

  shouldParkAfterFailedAcquire会判断是否对当前节点进行阻塞,阻塞之后只有当unpark后节点才会继续假如争夺锁的行列。

  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

  int ws = pred.waitStatus;

  if (ws == Node.SIGNAL)

  return true;

  if (ws > 0) {

  do {

  node.prev = pred = pred.prev;

  } while (pred.waitStatus > 0);

  pred.next = node;

  } else {

  compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

  }

  return false;

  }

  判断一个节点是否需要被阻塞是通过该节点的前继节点的状态判断的。

  如果前继节点状态为 singal ,则表示前继节点还在等待,当前节点需要继续被阻塞。返回true。

  如果前继节点大于0,则表示前继节点为取消状态。取消状态的节点不参与锁的竞争,直接跳过。返回false。

  如果前继节点时其他状态(0,PROPAGATE),不进行阻塞,表示当前节点需要重试尝试获取锁。返回false。

  shouldParkAfterFailedAcquire方法如果返回true,表示需要将当前节点阻塞,阻塞方法为parkAndCheckInterrupt。

  AbstractQueuedSynchronizer#parkAndCheckInterrupt

  private final boolean parkAndCheckInterrupt() {

  LockSupport.park(this);

  return Thread.interrupted();

  }

  阻塞是通过LockSupport进行阻塞,被阻塞的节点不参与锁的竞争(不在进行循环获取锁),只能被unpark后才继续竞争锁。

  而被阻塞的节点要被释放则依赖于unlock方法。

  unlock

  ReentrantLock 中unlock的实现是通过调用AQS的 AbstractQueuedSynchronizer#release 方法实现。

  public final boolean release(int arg) {

  if (tryRelease(arg)) {

  Node h = head;

  if (h != null && h.waitStatus != 0)

  unparkSuccessor(h);

  return true;

  }

  return false;

  }

  release调用tryRelease方法,tryRelease是在 ReentrantLock 中实现。

  ReentrantLock#tryRelease

  protected final boolean tryRelease(int releases) {

  int c = getState() - releases;

  if (Thread.currentThread() != getExclusiveOwnerThread())

  throw new IllegalMonitorStateException();

  boolean free = false;

  if (c == 0) {

  free = true;

  setExclusiveOwnerThread(null);

  }

  setState(c);

  return free;

  }

  tryRelease方法逻辑很简单,首先减去releases(一般为1)表示释放一个锁,如果释放后state=0表示释放锁成功,后续等待的节点可以获取该锁了。如果state!=0则表示该锁为重入锁,需要多次释放。

  当释放锁成功后(state=0),会对头结点的后继节点进行unpark。

  AbstractQueuedSynchronizer#unparkSuccessor

  private void unparkSuccessor(Node node) {

  int ws = node.waitStatus;

  if (ws < 0)

  compareAndSetWaitStatus(node, ws, 0);

  Node s = node.next;

  if (s == null || s.waitStatus > 0) {

  s = null;

  for (Node t = tail; t != null && t != node; t = t.prev)

  if (t.waitStatus <= 0)

  s = t;

  }

  if (s != null)

  LockSupport.unpark(s.thread);

  }

  unparkSuccessor见名知意适用于接触后面节点的阻塞状态。整个方法的逻辑就是找到传入节点的后继节点,将其唤醒(排除掉状态为cancel即waitStatus > 0的节点)。

  公平锁和非公平锁

  ReentrantLock 的构造方法接受一个可选的公平参数。当设置为 true 时,在多个线程的竞争时,倾向于将锁分配给等待时间最长的线程。

  public ReentrantLock(boolean fair) {

  sync = fair ? new FairSync() : new NonfairSync();

  }

  在多个锁竞争统一资源的环境下,AQS维护了一个等待队列,未能获取到锁的线程都会被挂到该队列中。如果使用公平锁则会从队列的头结点开始获取该资源。

  而根据代码在公平锁和非公平锁的实现的差别仅仅在于公平锁多了一个检测的方法。

  公平锁

  protected final boolean tryAcquire(int acquires) {

  //&hellip;

  if (c == 0) {

  if (!hasQueuedPredecessors() //!hasQueuedPredecessors()便是比非公平锁多出来的操作

  && compareAndSetState(0, acquires)) {

  setExclusiveOwnerThread(current);

  return true;

  }

  }

  //&hellip;

  return false;

  }

  hasQueuedPredecessors()

  public final boolean hasQueuedPredecessors() {

  Node t = tail; // Read fields in reverse initialization order

  Node h = head;

  Node s;

  return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());

  }

  方法逻辑很简单,就是如果等待队列还有节点并且排在首位的不是当前线程所处的节点返回true表示还有等待更长时间的节点。

关于“ReentrantLock是怎么基于AQS实现的”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“ReentrantLock是怎么基于AQS实现的”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网node.js频道。

--结束END--

本文标题: ReentrantLock是怎么基于AQS实现的

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

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

猜你喜欢
  • ReentrantLock是怎么基于AQS实现的
    这篇文章主要介绍了ReentrantLock是怎么基于AQS实现的的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇ReentrantLock是怎么基于AQS实现的文章都会有所收获...
    99+
    2024-04-02
  • 基于ReentrantLock的实现原理讲解
    目录ReentrantLock实现核心–AQS(AbstractQueuedSynchronizer)Node结构ReentrantLock实现分析二者关联NonfairSync分析...
    99+
    2024-04-02
  • java底层AQS实现类ReentrantLock锁的构成及源码解析
    目录引导语1、类注释2、类结构3、构造器 4、Sync 同步器4.1、nonfairTryAcquire 4.2、tryRelease5、FairSync 公平锁...
    99+
    2024-04-02
  • Java AQS的实现原理是什么
    这篇文章主要介绍“Java AQS的实现原理是什么”,在日常操作中,相信很多人在Java AQS的实现原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java AQS的实...
    99+
    2023-07-05
  • synchronized和ReentrantLock的基本原理是什么
    这篇文章主要介绍“synchronized和ReentrantLock的基本原理是什么”,在日常操作中,相信很多人在synchronized和ReentrantLock的基本原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作...
    99+
    2023-06-15
  • 基于 Agent的Python是怎么实现隔离仿真
    这篇文章给大家介绍基于 Agent的Python是怎么实现隔离仿真,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。我会向你介绍用基于 Agent 的模型理解复杂现象的威力。为此,我们会用到一些 Python,社会学的案例...
    99+
    2023-06-03
  • 怎么在JAVA中使用ReentrantLock实现并发
    这期内容当中小编将会给大家带来有关怎么在JAVA中使用ReentrantLock实现并发,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. 介绍结合上面的ReentrantLock类图,Reentrant...
    99+
    2023-06-15
  • JavaSE 6基于JSR105的XML签名是怎样实现的
    这篇文章将为大家详细讲解有关JavaSE 6基于JSR105的XML签名是怎样实现的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。我们开始分析一个实际的XML签名示例应用程序。  一、 密码...
    99+
    2023-06-03
  • 基于React.js和Node.js怎么实现SSR
    这篇文章主要介绍基于React.js和Node.js怎么实现SSR,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!基础概念SSR:即服务端渲染(Server Side Render) ...
    99+
    2024-04-02
  • 怎么实现php基于cookie登录
    小编给大家分享一下怎么实现php基于cookie登录,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!php制作记住密码自动登录的解决思路,其实也就是对session...
    99+
    2023-06-14
  • 基于Node.js怎么实现WebSocket通信
    这篇“基于Node.js怎么实现WebSocket通信”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“基于Node.js怎么实...
    99+
    2023-06-17
  • 基于dubbo分组group怎么实现
    本文小编为大家详细介绍“基于dubbo分组group怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“基于dubbo分组group怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。服务分组当一个接口有多...
    99+
    2023-07-05
  • 基于websocket的聊天功能怎么实现
    本篇内容主要讲解“基于websocket的聊天功能怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“基于websocket的聊天功能怎么实现”吧!   一...
    99+
    2024-04-02
  • 基于Tensorflow的图像识别怎么实现
    要实现基于Tensorflow的图像识别,可以按照以下步骤进行: 准备数据集:首先需要准备一个包含图像和对应标签的数据集,可以使...
    99+
    2024-03-13
    tensorflow
  • 基于spark的数据分析怎么实现
    要基于Spark实现数据分析,通常可以按照以下步骤进行: 数据准备:首先要将需要分析的数据加载到Spark中,可以从文件系统、数...
    99+
    2024-04-02
  • 基于React封装组件的实现步骤是怎样的
    这篇文章将为大家详细讲解有关基于React封装组件的实现步骤是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。前言很多小伙伴在第一次尝试封装组件时会和我一样碰到许多问题,比如人家的组件会...
    99+
    2023-06-21
  • 基于Python怎么实现对比Exce的工具
    这篇“基于Python怎么实现对比Exce的工具”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“基于Python怎么实现对比E...
    99+
    2023-06-29
  • 基于dubbo的分布式架构怎么实现
    本篇内容介绍了“基于dubbo的分布式架构怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言现在越来越多的互联网公司还是将自己公司的...
    99+
    2023-06-05
  • 基于hadoop的分布式爬虫怎么实现
    要实现基于Hadoop的分布式爬虫,可以按照以下步骤进行: 设计架构:首先需要设计分布式爬虫的架构,确定集群中各个节点的角色和任...
    99+
    2024-03-06
    hadoop
  • 基于Zookeeper怎么实现分布式锁
    这篇文章主要介绍“基于Zookeeper怎么实现分布式锁”,在日常操作中,相信很多人在基于Zookeeper怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”基于Zookeeper怎么实现分布式锁...
    99+
    2023-06-22
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作