
面试刷题22:CAS和AQS是什么?
发布日期:2021-05-09 06:50:12
浏览次数:8
分类:博客文章
本文共 3036 字,大约阅读时间需要 10 分钟。
java并发包提供的同步工具和线程池,底层是基于什么原理来设计和实现的呢?这个非常重要。 我是李福春,我在准备面试,今天的题目是: CAS和AQS是什么? 答:CAS是一系列的操作集合,获取当前值进行计算,如果当前值没有改变,表示线程没有被占用,直接更新成功,否则,进行重试或者返回成功或者失败。 他是java并发工具包中lock-free的基础吗,依赖底层的cpu提供的特定指令实现。底层依赖于Unsafe的本地对象来实现。AQS: 全称是AbstractQueuedSynchronizier,抽象队列同步器;他是各种同步工具锁的基础,比如ReentrantLock, CyclicBairier都是基于AQS来实现的。
CAS
以AtomicInteger为例子来分析一下CAS的应用。首先看内部结构: 然后分析一下他的一个利用CAS实现的原子操作。```javapublic final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!weakCompareAndSetInt(o, offset, v, v + delta)); return v;}```这是一个自旋操作,利用Unsafe比较内存的偏移量,基于cpu指令保证修改的原子可见性。 使用CAS也是有缺点的: 1,自旋次数是假定冲突情况很少的理想情况,但是情况不理想容易过度消耗CPU; 2, ABA问题,一般使用加版本号的 AtomicStampedRefence来搞定; 在实际的工作中如何使用CAS来保证同步操作呢? 可以基于AtomicFieldLongUpdater来实现。比如下面是一个基于CAS实现的同步更新数据库索引的例子,代码如下:public static class AtomicBTreePartition{ private volatile long lock; private static final AtomicLongFieldUpdaterlockFieldUpdater =AtomicLongFieldUpdater .newUpdater(AtomicBTreePartition.class,"lock"); public void acquireLock(){ long t = Thread.currentThread().getId(); while (!lockFieldUpdater.compareAndSet(this, 0, 1)){ //数据库操作 } } public void releaseLock(){ } }
AQS
她的组成核心主要有3个部分。
下面以ReentrantLock的非公平锁为例,分析一下它的加锁和释放锁的实现。加锁操作代码:public void lock() { sync.acquire(1); }
继续跟进:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();}非公平锁的获取锁操作:
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState();// 获取当前AQS内部状态量 if (c == 0) { // 0表示无人占有,则直接用CAS修改状态位, if (compareAndSetState(0, acquires)) { // 不检查排队情况,直接争抢 setExclusiveOwnerThread(current); //并设置当前线程独占锁 return true; } } else if (current == getExclusiveOwnerThread()) { //即使状态不是0,也可能当前线程是锁持有者,因为这是再入锁 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false;}
排队竞争:
final boolean acquireQueued(final Node node, int arg) { boolean interrupted = false; try { for (;;) {// 循环 final Node p = node.predecessor();// 获取前一个节点 if (p == head && tryAcquire(arg)) { // 如果前一个节点是头结点,表示当前节点合适去tryAcquire setHead(node); // acquire成功,则设置新的头节点 p.next = null; // 将前面节点对当前节点的引用清空 return interrupted; } if (shouldParkAfterFailedAcquire(p, node)) // 检查是否失败后需要park interrupted |= parkAndCheckInterrupt(); } } catch (Throwable t) { cancelAcquire(node);// 出现异常,取消 if (interrupted) selfInterrupt(); throw t; }
小结
本节介绍了CAS和AQS的概念,然后以AtomicInteger为例切入原子操作是怎么利用CAS来保证的。最后,以ReentrantLock的加锁操作,跟进了是如何利用AQS来保证内部的不同操作的。算是初步探究了同步的实现原理。原创不易,转载请注明出处。
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2025年04月15日 00时54分50秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
026 模块3-random库的使用
2019-03-06
027 实例6-圆周率的计算
2019-03-06
小白专场-树的同构-python语言实现
2019-03-06
191006
2019-03-06
08-03 细分构建机器学习应用程序的流程-流程简介
2019-03-06
B-概率论-条件概率
2019-03-06
191107
2019-03-06
191120
2019-03-06
191123
2019-03-06
第4章 字符串、数组和特殊矩阵
2019-03-06
0608-nn和autograd的区别
2019-03-06
MYSQL 数据库结构优化
2019-03-06
leetcode 一些算法题及答案
2019-03-06
spring 整合 ActiveMQ
2019-03-06
PHP 取前一天或后一天、一个月时间
2019-03-06
Kafka 分布式的,基于发布/订阅的消息系统
2019-03-06
Spring Bean的加载
2019-03-06
Web笔记(一) Web 简介与开发环境搭建
2019-03-06
Java基础回顾-缓冲流
2019-03-06
JSONPath小试牛刀之Snack3
2019-03-06