AQS详解

AQS概念

AQS(AbstractQuenedSynchronizer)抽象队列同步器,是⼀种锁机制。 在java.util .concurrent⼯具包中,这个包简称JUC。

AQS底层实现原理

AQS维护了⼀个 volatile 修饰的共享变量 state 和⼀个 FIFO 线程等待队列,多线程争⽤资源被阻塞的时候就会进⼊这个队列。state 是共享变量,其访问⽅式有如下三种:getState()、setState()、compareAndSetState(),通过尝试获取共享变量 state 的结果来对线程的状态作出处理。

AQS 是将每⼀条请求共享资源的线程封装成⼀个 CLH 队列(虚拟的双向队列)的⼀个结点来实现锁的分配。根据 volatile 修饰的 state 共享变量,线程通过 CAS(Compare and swap) 去改变状态。如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是基于 CLH 锁 (Craig, Landin, and Hagersten locks) 实现的。

这也是网上的一种说法,参考一下:
AQS是.NET Framework中提供的一种同步机制,意为"Advanced Synchronization (高级同步)",
可以用来控制多线程操作中各个线程之间的访问顺序和优先级。 AQS的基本思想是,它提供一种资源管理的方式,
可以让多个线程安全地共享同一个资源,通过AQS提供的同步机制,可以让线程按照一定的次序和优先级进入和离开共享资源,
从而保证线程操作的正确性和有序性。主要包括信号量(Semaphore)、倒计时门闩(CountdownEvent)、读写锁(ReaderWriterLock)等同步类。 
除此之外,AQS还提供了自定义同步机制的接口IWaitableTimerIAsyncWaitHandle等。
AQS是Java中也有的机制,称作AbstractQueuedSynchronizer

原理总结:简单来说 AQS 就是一种同步机制,可以让多个线程安全地共享同一个资源,AQS 是基于 CLH 队列锁和 被 volatile 修饰的共享变量 state 实现的,线程通过 CAS 操作改变状态符状态,成功则获取锁,失败则被加入等待队列等待被唤醒

AQS(AbstractQueuedSynchronizer)的核心原理图如下:

在这里插入图片描述
AQS 使用 int 成员变量 state 表示同步状态,通过内置的 线程等待队列 来完成获取资源线程的排队工作。state 变量由 volatile 修饰,用于展示当前临界资源的获锁情况。// 共享变量,使用volatile修饰保证线程可见性

private volatile int state;

另外,状态信息 state 可以通过 protected 类型的getState()、setState()和compareAndSetState() 进行操作。并且,这几个方法都是 final 修饰的,在子类中无法被重写。

//返回同步状态的当前值
protected final int getState() {
     return state;
}
 // 设置同步状态的值
protected final void setState(int newState) {
     state = newState;
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
      return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

以 ReentrantLock 为例,state 初始值为 0,表示未锁定状态。A 线程 lock() 时,会调用 tryAcquire() 独占该锁并将 state+1 。此后,其他线程再 tryAcquire() 时就会失败,直到 A 线程 unlock() 到 state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(state 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多少次,这样才能保证 state 是能回到零态的。

再以 CountDownLatch 以例,任务分为 N 个子线程去执行,state 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后countDown() 一次,state 会 CAS(Compare and Swap) 减 1。等到所有子线程都执行完后 (即 state=0 ),会 unpark() 主调用线程,然后主调用线程就会从 await() 函数返回,继续后余动作。

CLH队列锁

CLH 锁是对自旋锁的一种改进,是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系),暂时获取不到锁的线程将被加入到该队列中。AQS 将每条请求共享资源的线程封装成一个 CLH 队列锁的一个结点(Node)来实现锁的分配。在 CLH 队列锁中,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。CLH 队列锁结构如下图所示:

在这里插入图片描述
如果想要详细了解 CLH 锁,可以参考这篇文章——Java AQS 核心数据结构-CLH 锁

AQS 资源共享方式

AQS 定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。一般来说,自定义同步器的共享方式要么是独占,要么是共享,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路上阡陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值