Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >AQS --- 渐入佳境

AQS --- 渐入佳境

作者头像
贪挽懒月
发布于 2021-07-23 08:16:02
发布于 2021-07-23 08:16:02
27600
代码可运行
举报
文章被收录于专栏:JavaEEJavaEE
运行总次数:0
代码可运行

上一讲了解了 AQS 是什么,接下来看看它到底是怎样的结构。

一. 工作原理

AQS 使用一个 volatile 的 int 类型的成员变量来表示同步状态,通过内置的 FIFO 队列来完成资源获取和排队工作,将每条要去抢占资源的线程封装成一个 node 节点来实现锁的分配,通过 CAS 来完成对 state 值的修改。

HashMap 进行 put 的时候,也不是直接存储 key value 键值对,而是将 key value 键值对封装成 Node 节点,然后用数组 + 链表 + 红黑树存储 Node。AQS 也类似,将要抢占资源的 Thread 封装成 Node节点。

二. 相关源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
   
    static final class Node {
       ……
       volatile int waitStatus;
       volatile Node prev;
       volatile Node next;
       volatile Thread thread;
       ……
    }

    private transient volatile Node head;
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;
}

看到这个是不是就清清楚楚明明白白真真切切了。首先 AQS 外层是 state + CLH 队列,state 表示同步的状态,默认是0,为0时表示可以获取锁,不为0时,线程就得老老实实到队列中排队去;CLH 队列就是一个有头结点和尾结点的双端队列,如下图:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           +------+  prev +-----+       +-----+
      head |      | <---- |     | <---- |     |  tail
           +------+       +-----+       +-----+

AQS 的内层是一个 Node内部类,这个 Node 类主要有两个指针 prev 和 next、一个 waitStatus 表示线程的状、,一个 Thread 类型的变量保存等待的线程。

三. 从 ReentrantLock 看 AQS:

之前说了 AQS 是 JUC 并发包的基石,那就从我们接触最多的 ReentrantLock 入手,揭开它的神秘面纱。

先来看看 ReentrantLock 的结构图:

结构图

首先它实现了 Lock 接口,其内部主要是一个 Sync 内部类,这个内部类又有两个子类,一个 FairSync 和一个 NonfairSync,分别用来实现公平锁和非公平锁。而这个 Sync 内部类,又是 AbstractQueuedSynchronizer 的子类。

1. 我们 new ReentrantLock 的时候做了什么事?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
 public ReentrantLock() {
     sync = new NonfairSync();
 }

通过这个构造方法可以知道,实际上是构建了一个非公平锁。如果 new 的时候传了 true,调用的构造方法就是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
 public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
 }

所以传的是 true,构建的就是公平锁。

2. 公平和非公平有什么区别?

非公平锁源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

公平锁源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        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;
}

乍一看两段代码好像没啥不一样,其实不同之处在,if (c == 0)这段判断中。公平锁多了一个判断条件,即!hasQueuedPredecessors(),看看这个方法的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    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());
}

这个方法也很简单,首先是头节点不等于尾节点,然后就是头节点的下一个节点为空或者头节点的下一个节点保存的 Thread 不等于当前的 Thread。简单地说就是看队列中有没有除了当前 Thread 以为的 Thread 在等待获取锁,有就返回 true,否则返回 false。所以公平锁就是多了这个判断,其他都一样。

下一篇文章将会从源码层面分析 ReentrantLock 的加锁过程,敬请期待!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java-ReentrantLock-公平锁源码分析
在了解AQS后,那应该怎么了解AQS的最佳实践那,我想再也没有Java官方的实践更加优秀了,这次我打算重新拿出系统源代码,并将其总结成一系列文章,以供将来查看.
颍川
2021/12/06
2200
不能再被问住了!ReentrantLock 源码、画图一起看一看!
" 在阅读完 JUC 包下的 AQS 源码之后,其中有很多疑问,最大的疑问就是 state 究竟是什么含义?并且 AQS 主要定义了队列的出入,但是获取资源、释放资源都是交给子类实现的,那子类是怎么实现的呢?下面开始了解 ReentrantLock。 "
程序员小航
2020/11/23
3360
不能再被问住了!ReentrantLock 源码、画图一起看一看!
高并发编程-ReentrantLock公平锁深入解析
ReentrantLock是一个可重入的互斥锁,它不但具有synchronized实现的同步方法和同步代码块的基本行为和语义,而且具备很强的扩展性。ReentrantLock提供了公平锁和非公平锁两种实现,在默认情况下构造的ReentrantLock实例是非公平锁,可以在创建ReentrantLock实例的时候通过指定公平策略参数来指定是使用公平锁还是非公平锁。多线程竞争访问同一资源的时,公平锁倾向于将访问权授予等待时间最长的线程,但需要明确的是公平锁不能保证线程调度的公平性。和非公平锁相比,公平锁在多线程访问时总体吞吐量偏低,但是获得锁和保证锁分配的均衡性差异较小。本篇将基于JDK7深入源码解析公平锁的实现原理。
JavaQ
2018/09/14
9680
3分钟带你搞懂 AQS 原理设计
在之前的文章中,我们介绍了 ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor 等并发工具类的使用方式,它们在请求共享资源的时候,都能实现线程同步的效果。
Java极客技术
2024/01/12
3310
3分钟带你搞懂 AQS 原理设计
并发编程系列-从底层源码剖析AQS的来龙去脉!(通俗易懂)
本文是作者的第一篇文章,目的就是可以分享自己个人的一些技术上的心得体会以及找寻志同道合的人来共同讨论技术。
用户10873361
2024/06/15
3920
并发编程系列-从底层源码剖析AQS的来龙去脉!(通俗易懂)
JUC包深度讲解AQS(AbstractQueuedSynchronizer)源码
AQS(AbstractQueuedSynchronizer),抽象队列同步器是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,内置FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量(state)表示持有锁的状态Reentrantlock、CountDownLatch、等juc锁和工具类.
小明爱吃火锅
2023/12/06
2631
AQS很难,面试不会?看我一篇文章吊打面试官
大家好,我是小高先生。在这篇文章中,我将和大家深入探索Java并发包(JUC)中最为核心的概念之一 -- AbstractQueuedSynchronizer(AQS)。AQS不仅是构建JUC底层体系的基石,更是掌握并发编程不可或缺的一环,也是当下面试中常考问题。如果我们在学习JUC时忽略了AQS,那就像是基督教徒失去了耶路撒冷那般不可想象,它的重要性自不必多言。我将以ReentrantLock为切入点,深入讨论AQS的原理和使用。本文内容多且复杂,为了方便大家学习,我在文章最后放置了ReentrantLock的流程图,有助于大家更好的掌握AQS。
小高先生
2024/03/15
2341
AQS很难,面试不会?看我一篇文章吊打面试官
【死磕Java并发】—–J.U.C之重入锁:ReentrantLock
此篇博客所有源码均来自JDK 1.8 ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率。 API介绍如下: 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定
用户1655470
2018/04/26
7660
【死磕Java并发】—–J.U.C之重入锁:ReentrantLock
JUC并发—5.AQS源码分析一
ReentractLock是重入锁,属于排他锁,功能和synchronized类似。但是在实际中,其实比较少会使用ReentrantLock。因为ReentrantLock的实现及性能和syncrhonized差不多,所以一般推荐使用synchronized而不是ReentrantLock。
东阳马生架构
2025/04/24
1010
ReetrantLock源码分析
ReentrantLock类的大部分逻辑,都是其均继承自AQS的内部类Sync实现的
Java宝典
2020/12/04
3480
从ReentrantLock的实现看AQS的原理及应用
AQS作为JUC中构建锁或者其他同步组件的基础框架,应用范围十分广泛,这篇文章会带着大家从可重入锁一点点揭开AQS的神秘面纱。
美团技术团队
2019/12/10
1.7K2
从ReentrantLock的实现看AQS的原理及应用
带你学习 ReentrantLock
一个线程可多次获取锁,但同时也要释放相同的次数,否则该线程将持续拥有锁,其他线程将无法进入临界区。
大发明家
2021/12/18
3260
java架构之路(多线程)AQS之ReetrantLock显示锁的使用和底层源码解读
  说完了我们的synchronized,这次我们来说说我们的显示锁ReetrantLock。
小菜的不能再菜
2020/02/21
4650
ReentrantLock实现原理
建议和上一篇分享结合着看:深入理解AbstractQueuedSynchronizer
Java识堂
2019/08/13
4610
多线程应用 - 超详细的AQS详情
这篇主要是分析下AQS的原理,说实话挺难懂的。写文章的时候也难以下手。先解释下AQS是什么。
虞大大
2020/08/26
5160
多线程应用 - 超详细的AQS详情
ReentrantLock 实现原理
使用 synchronized 来做同步处理时,锁的获取和释放都是隐式的,实现的原理是通过编译后加上不同的机器指令来实现。
爱明依
2022/04/01
1540
了解ReentrantLock
ReetrantLock是一个可重入的独占锁,主要有两个特性,一个是支持公平锁和非公平锁,一个是可重入。 ReetrantLock实现依赖于AQS(AbstractQueuedSynchronizer) ReetrantLock主要依靠AQS维护一个阻塞队列,多个线程对加锁时,失败则会进入阻塞队列。等待唤醒,重新尝试加锁。下图是其类图
崔笑颜
2020/06/08
5100
【JDK1.8】JUC——ReentrantLock
在之前的几篇中,我们回顾了锁框架中比较重要的几个类,他们为实现同步提供了基础支持,从现在开始到后面,就开始利用之前的几个类来进行各种锁的具体实现。今天来一起看下ReentrantLock,首先来看一下Java doc 上对ReentrantLock的解释:
joemsu
2018/08/21
4450
【JDK1.8】JUC——ReentrantLock
AQS之独占锁
AbstractQueuedSynchronizer,抽象类,模板模式,子类通过实现其模板方法,从而实现不同的同步器,例如: ReentrantLock ReentrantReadWriteLock BlockingQueue CountDownLatch
spilledyear
2020/01/02
6100
ReentrantLock
CAS是一种无锁算法。有3个操作数:内存值V、旧的预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
hhss
2021/02/12
4440
ReentrantLock
相关推荐
Java-ReentrantLock-公平锁源码分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验