欢迎光临
我们一直在努力

Java八股:什么是AQS?CAS和AQS的操作关系

什么是AQS(AbstractQueuedSynchronizer)

一句话:

AQS 是一个用来构建锁和同步器的“线程排队管理框架”。

他主要复制三件事:

  • 管理锁状态
  • 管理线程等待队列
  • 管理线程阻塞与唤醒

AQS 的整体结构

AQS

├── state(同步状态)
├── CLH队列(等待线程队列)
└── CAS操作(保证线程安全)

第一个核心:state(锁状态)

AQS内部有一个变量:

private volatile int state;

他表示锁的状态
在互斥锁中

state = 0 没有线程持有锁
state = 1 有线程持有锁

线程想要获取锁时:会用CAS操作

CAS(state,0,1)

成功说明拿到锁;失败说明锁被别人占用了。

第二个核心:等待队列

如果没抢到锁;就推进到第二步。

进入等待队列

AQS使用的是:

CLH队列

结构:Head -> Node -> Node -> Node
每给Node表示:一个等待锁的进程

比如:

线程1 拿到锁
线程2 排队
线程3 排队
线程4 排队

队列结构:

Head -> T2 -> T3 -> T4

第三个核心:线程阻塞和唤醒

线程进入等待队列中不会一直运行,而是:

被挂起(park)

AQS使用:

LockSupport.park()

使线程进入:休眠状态

等待锁释放时:

LockSupport.unpark()

唤醒队列中的线程。

流程图:

线程抢锁

失败

进入队列

park阻塞

锁释放

unpark唤醒

完整执行流程

三个线程:

T1
T2
T3

第一步
T1来获取锁:

state=0
cas成功

T1 获得锁。

第二步
T2 来获取锁:

state = 1
CAS失败

于是:T2进入队列

队列:

Head -> T2

T2:

park()

睡眠

第三步
T3 来获取锁
同样失败:

Head -> T2 -> T3

T3 也:

park()

第四步
T1 释放锁:

state = 0

然后唤醒队列:

unpark(T2)

T2被唤醒后:再次尝试:CAS(state,0,1)
成功获得锁。

AQS 是怎么被使用的

AQS本身不是锁。
是:

锁的实现框架

很多并发工具都是基于AQS实现的:

ReentrantLock
Semaphore
CountDownLatch
ReentrantReadWriteLock

这些类都会:继承AQS
然后实现两个核心方法:

tryAcquire()
tryRelease()

AQS负责:

排队
阻塞
唤醒

AQS 最核心的设计思想

在这里插入图片描述

面试

AQS是java并发包中的同步框架,他通过一个volatile的state变量表示同步状态,并使用CLH队列管理等待线程,通过CAS操作保证线程安全,同时利用park/unpark实现线程阻塞与唤醒,从而为ReentrantLock、Semaphore、CountDownLatch 等同步器提供基础实现。

CAS和AQS的关系

上面我们讲到了CAS,现在讲讲CAS和AQS的关系
为了保证线程安全我们通常有两种操作;
第一:加锁
第二无锁并发
这就是CAS

CAS是一种乐观锁思想

我们可以理解为:

我任务没人会修改这个值了,如果有人改了,我就重试。

什么是CAS

CAS操作需要三个值:

CAS(内存值, 期望值, 新值)

执行逻辑:

如果 内存值 == 期望值
修改为 新值
否则
修改失败

例如:

state = 0

线程想改为:

1

执行:

CAS(0,1)

如果:

state 还是 0

说明没人改过:

成功

如果:

state 已经变成 1

说明别人改了:

失败

线程就会:

重新尝试

CAS 和 AQS 的关系

AQS 在修改 state 的时候,并不会用:synchronized
而是用CAS

CAS 是 AQS 修改 state 的原子操作基础,而 AQS 是基于 CAS 构建的线程同步框架。

CAS → 原子操作工具
AQS → 并发框架

赞(0)
未经允许不得转载:171主机测评 » Java八股:什么是AQS?CAS和AQS的操作关系
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址