首页 > 编程知识 正文

cas无锁技术的理解,cas锁底层原理

时间:2023-05-06 05:10:29 阅读:129234 作者:1451

比较器(cas )是什么是cascas (compareandswap ),也就是比较交换。 一种解决多线程并行情况下使用锁所导致的性能损失的机制,CAS操作包括三个操作数——内存位置(v )、预期原始值(a )和新值(b )。 如果内存位置的值与预期的原始值匹配,则处理器会自动将该位置的值更新为新值。 否则,处理器什么都不做。 在任一情况下,在CAS命令之前返回该位置的值。 CAS有效地表示:“我认为位置v应该包含值a; 如果包含此值,则将b置于此位置; 否则,请不要更改该位置,只告诉我该位置的当前值。

java.util.concurrent包使用CAS来提供与同步同步锁定不同的乐观锁定。

原子类之一为AtomicInteger,java对于CAS支持的操作是利用Unsafe类库的compareAndSwapInt备注(该类不是提供给用户的类Unsafe,getUnsafe的代码中限制了只有启动类加载器Bootstrap ClassLoader加载的class才能访问他),AtomicInteger的核心就是一个CAS算法(CompareAndSwap),比较并交换算法,此算法是由unsafe的底层代码实现,它是一个原子的操作,原理就是:如果内存中的实际值与update值相同,则将实际值更新为expect值,反之则返回失败,由上层系统循环获取实际值后,再次调用此CAS算法:

CAS应用

CAS有三个操作数、存储值v、旧期望值a和要修改的新值b。 此外,仅当期望值a和存储器值v相同时,将存储器值v改变为b,否则不执行任何操作。

在JAVA中,sun.misc.Unsafe类提供了实现此CAS的硬件级原子操作。 java.util.concurrent包下的许多类都使用此Unsafe.java类的CAS操作。 我们不在此讨论Unsafe.java的具体实现。

许多在Java.util.concurrent.atomic (CAS的典型APP应用程序)中打包的类都是使用cas操作实现的。 (eg.AtomicInteger.java,AtomicBoolean,AtomicLong )。 使用AtomicInteger.java实现的一部分来大致介绍这些原子类的实现。

publicclassatomicintegerextendsnumberimplementsjava.io.serializable { privatestaticfinallongserialversionuid=621479024344 setuptouseunsafe.compareandswapintforupdatesprivatestaticfinalunsafeunsafe=unsafe.get unsafe (专用卷内值); //初始int大小//省略某些代码.//在带参数的构造函数中,初始int大小publicatomicinteger (int initial value ) {value=initialValue; //无参数构造函数,初始int大小为0public AtomicInteger () )//当前public final int get ) ) {返回值; //设定值为newvaluepublicfinalvoidset (int new value ) ) {value=newValue; //返回旧值,新值为newvaluepublicfinalintgetandset (int new value ) {/***现在,我们继续使用for循环在CAS操作中设置新值。 * CAS实现和锁定之间的关系有点类似于乐观锁定和悲观锁定之间的关系。 * */for () ) ) ) ) ) ); ({int current=get ); if (比较性(当前,新值) )返回当前; }//原子设置的新值为update,expect是预期的当前值publicfinalbooleancompareandset (int expect,int update ) return unsafe.compareandswapate ; ({int current=get ); int next=current 1; if (比较性(当前,下一步) )返回当前; }//这里省略了部分代码,但剩下的代码大致实现原理相似}

通常,在竞争不是特别激烈的时候,您会发现使用此包进行原子操作比使用同步关键字更高效。 (getAndSet ) )如果资源竞争非常激烈,这是

个for循环可能换持续很久都不能成功跳出。不过这种情况可能需要考虑降低资源竞争才是)。 
在较多的场景我们都可能会使用到这些原子类操作。一个典型应用就是计数了,在多线程的情况下需要考虑线程安全问题。通常第一映像可能就是:

 

public class Counter {private int count;public Counter(){}public int getCount(){return count;}public void increase(){count++;}}

 

上面这个类在多线程环境下会有线程安全问题,要解决这个问题最简单的方式可能就是通过加锁的方式,调整如下:

 

public class Counter {private int count;public Counter(){}public synchronized int getCount(){return count;}public synchronized void increase(){count++;}}

 

这类似于悲观锁的实现,我需要获取这个资源,那么我就给他加锁,别的线程都无法访问该资源,直到我操作完后释放对该资源的锁。我们知道,悲观锁的效率是不如乐观锁的,上面说了Atomic下的原子类的实现是类似乐观锁的,效率会比使用 synchronized 关系字高,推荐使用这种方式,实现如下:

 

public class Counter {private AtomicInteger count = new AtomicInteger();public Counter(){}public int getCount(){return count.get();}public void increase(){count.getAndIncrement();}}

 

AQS(AbstractQueuedSynchronizer) 什么是AQS

AQS(AbstractQueuedSynchronizer),AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架。这个抽象类被设计为作为一些可用原子int值来表示状态的同步器的基类。如果你有看过类似 CountDownLatch 类的源码实现,会发现其内部有一个继承了 AbstractQueuedSynchronizer 的内部类 Sync。可见 CountDownLatch 是基于AQS框架来实现的一个同步器.类似的同步器在JUC下还有不少。(eg. Semaphore)

AQS用法

如上所述,AQS管理一个关于状态信息的单一整数,该整数可以表现任何状态。比如, Semaphore 用它来表现剩余的许可数,ReentrantLock 用它来表现拥有它的线程已经请求了多少次锁;FutureTask 用它来表现任务的状态(尚未开始、运行、完成和取消)

 

To use this class as the basis of a synchronizer, redefine the* following methods, as applicable, by inspecting and/or modifying* the synchronization state using {@link #getState}, {@link* #setState} and/or {@link #compareAndSetState}:** <ul>* <li> {@link #tryAcquire}* <li> {@link #tryRelease}* <li> {@link #tryAcquireShared}* <li> {@link #tryReleaseShared}* <li> {@link #isHeldExclusively}* </ul>

 

如JDK的文档中所说,使用AQS来实现一个同步器需要覆盖实现如下几个方法,并且使用getState,setState,compareAndSetState这几个方法来设置获取状态 

1. boolean tryAcquire(int arg) 2. boolean tryRelease(int arg) 3. int tryAcquireShared(int arg) 4. boolean tryReleaseShared(int arg) 5. boolean isHeldExclusively()

以上方法不需要全部实现,根据获取的锁的种类可以选择实现不同的方法,支持独占(排他)获取锁的同步器应该实现tryAcquire、 tryRelease、isHeldExclusively而支持共享获取的同步器应该实现tryAcquireShared、tryReleaseShared、isHeldExclusively。下面以 CountDownLatch 举例说明基于AQS实现同步器, CountDownLatch 用同步状态持有当前计数,countDown方法调用 release从而导致计数器递减;当计数器为0时,解除所有线程的等待;await调用acquire,如果计数器为0,acquire 会立即返回,否则阻塞。通常用于某任务需要等待其他任务都完成后才能继续执行的情景。源码如下:

 

public class CountDownLatch {/*** 基于AQS的内部Sync* 使用AQS的state来表示计数count.*/private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {// 使用AQS的getState()方法设置状态setState(count);}int getCount() {// 使用AQS的getState()方法获取状态return getState();}// 覆盖在共享模式下尝试获取锁protected int tryAcquireShared(int acquires) {// 这里用状态state是否为0来表示是否成功,为0的时候可以获取到返回1,否则不可以返回-1return (getState() == 0) ? 1 : -1;}// 覆盖在共享模式下尝试释放锁protected boolean tryReleaseShared(int releases) {// 在for循环中Decrement count直至成功;// 当状态值即count为0的时候,返回false表示 signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}}private final Sync sync;// 使用给定计数值构造CountDownLatchpublic CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);}// 让当前线程阻塞直到计数count变为0,或者线程被中断public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}// 阻塞当前线程,除非count变为0或者等待了timeout的时间。当count变为0时,返回truepublic boolean await(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}// count递减public void countDown() {sync.releaseShared(1);}// 获取当前count值public long getCount() {return sync.getCount();}public String toString() {return super.toString() + "[Count = " + sync.getCount() + "]";}

AQS源码解析:https://www.cnblogs.com/waterystone/p/4920797.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。