博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面试刷题22:CAS和AQS是什么?
阅读量:4211 次
发布时间:2019-05-26

本文共 3061 字,大约阅读时间需要 10 分钟。

image.png

java并发包提供的同步工具和线程池,底层是基于什么原理来设计和实现的呢?这个非常重要。
我是李福春,我在准备面试,今天的题目是:
CAS和AQS是什么?
答:CAS是一系列的操作集合,获取当前值进行计算,如果当前值没有改变,表示线程没有被占用,直接更新成功,否则,进行重试或者返回成功或者失败。 他是java并发工具包中lock-free的基础吗,依赖底层的cpu提供的特定指令实现。底层依赖于Unsafe的本地对象来实现。

AQS: 全称是AbstractQueuedSynchronizier,抽象队列同步器;他是各种同步工具锁的基础,比如ReentrantLock, CyclicBairier都是基于AQS来实现的。

CAS

以AtomicInteger为例子来分析一下CAS的应用。
首先看内部结构:

image.png

然后分析一下他的一个利用CAS实现的原子操作。 ```java public 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 AtomicLongFieldUpdater
lockFieldUpdater =AtomicLongFieldUpdater .newUpdater(AtomicBTreePartition.class,"lock"); public void acquireLock(){ long t = Thread.currentThread().getId(); while (!lockFieldUpdater.compareAndSet(this, 0, 1)){ //数据库操作 } } public void releaseLock(){ } }

AQS

她的组成核心主要有3个部分。

file

下面以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来保证内部的不同操作的。
算是初步探究了同步的实现原理。
image.png

原创不易,转载请注明出处,让我们互通有无,共同进步,欢迎多沟通交流

你可能感兴趣的文章
解决 Asp.net 中,url传参乱码 方法之一:(UrlDecode)
查看>>
pdf的转换网址:
查看>>
c++设计模式之三~抽象工厂模式
查看>>
c++设计模式之单例模式
查看>>
c++设计模式之建造者模式
查看>>
c++设计模式之原型模式
查看>>
c++设计模式之适配器模式
查看>>
c++设计模式之桥接模式
查看>>
c++设计模式之装饰模式
查看>>
Mysql学习笔记(八)- 两个简单实用的优化方法
查看>>
mysql学习笔记(九)- 增删改查的优化
查看>>
Jenkins学习笔记(一)
查看>>
AtomicInteger源码解析
查看>>
CopyOnWriteArraySet源码学习
查看>>
ThreadLocal学习笔记
查看>>
用talib实现基于emv的简易量化投资策略
查看>>
LongAdder源码解析
查看>>
Talib学习笔记(二)- 价格指数学习
查看>>
CAS机制是什么?
查看>>
Semaphore源码解析
查看>>