您当前位置:资讯中心 >开发 >浏览文章

没看过AQS源码,别说精通Java并发编程

来源: 一灯架构 日期:2024/3/1 16:45:49 阅读量:(0)

前言

AQS 全称 AbstractQueuedSynchronizer(抽象队列同步器),旨在作为创建锁和其他同步机制的基础,常见的同步锁 ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier等都是基于 AQS 实现的。所以只有了解了AQS的实现原理,才能更好学习使用其他同步锁。

AQS的源码逻辑比较复杂,很多开发者看见就头疼,逻辑众多,无法梳理清楚。原因就是开发者梳理源码的步骤出错了,刚开始就看AQS的加锁、释放锁逻辑,陷入细节中不能自拔。正确的做法是,先整体后局部,先框架后细节。下面就带着大家一下分析AQS源码,保证清晰易懂。

AQS加锁流程

为什么一上来先看AQS的加锁流程,先要理解AQS的框架设计,才能去看具体的源码。

问个问题,如果让你设计一个同步锁,你会怎么设计?

肯定先要梳理一下需求,需求没有梳理清楚,就别谈开发了。

我理解的设计一个同步锁,需要满足以下需求:

  1. 当多个线程竞争同一个临界资源的时候,只有一个线程可以获取到临界资源,其他线程只能等待。所以这里我们需要一个状态state用来记录临界资源是否被加锁和加锁的次数,还需要记录一下这个资源是被哪个线程持有,字段名叫做exclusiveOwnerThread。还需要一个队列,用来存储等待获取资源的线程,这个队列我们叫做同步队列。
  2. 持有资源的线程可以主动挂起自己(调用await()方法),并且释放锁,然后等待被其他线程唤醒。所以这里需要一个队列存储需要被唤醒的线程,这个队列我们叫做条件队列。
  3. 在条件队列中线程被唤醒后,并不能立即获取到锁,还需要跟同步队列中线程一起竞争锁。所以在条件队列中被唤醒的线程,需要转移到同步队列。

至此,我们梳理清楚了AQS的加锁需求,而实际上AQS的加锁流程跟上面的需求完全一致,下面用一张图来表示。

图片图片

AQS的数据结构

看一下AQS内部的架构设计和包含的属性。

// AQS继承自AbstractOwnableSynchronizer,为了记录哪个线程占用锁
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
  
    // 同步状态,0表示无锁,每次加锁+1,释放锁-1
    private volatile int state;

    // 同步队列的头尾节点
    private transient volatile Node head;
    private transient volatile Node tail;

    // Node节点,用来包装线程,放到队列中
    static final class Node {
        // 节点中的线程
        volatile Thread thread;

        // 节点状态
        volatile int waitStatus;

        // 同步队列的前驱节点和后继节点
        volatile Node prev;
        volatile Node next;

        // 条件队列的后继节点或者同步队列的共享/排他模式
        Node nextWaiter;
    }

    // 条件队列
    public class ConditionObject implements Condition {
        // 条件队列的头尾节点
        private transient Node firstWaiter;
        private transient Node lastWaiter;
    }
}
关键字:
声明:我公司网站部分信息和资讯来自于网络,若涉及版权相关问题请致电(63937922)或在线提交留言告知,我们会第一时间屏蔽删除。
有价值
0% (0)
无价值
0% (10)

分享转发:

发表评论请先登录后发表评论。愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。