博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初识CAS
阅读量:4170 次
发布时间:2019-05-26

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

CAS(compare and swap)实质就是比较交换策略,设计并发算法时常用到的一种技术,java.util.concurrent 包基本都建立在 CAS 之上,现代的处理器基本都支持 CAS,只是不同厂商实现不同而已;CAS 有内存值V、旧的预期值A、要修改的值B三个操作数,当且仅当预期值A和内存值V相同时才会将内存值修改为B并返回 true,否则什么也不做且返回 false。

CAS 是通过 Unsafe 类来实现的,Unsafe 提供了硬件级别的原子操作,关于 CAS 的方法主要是 native 的 compareAndSwapObject、compareAndSwapInt、compareAndSwapLong,其比较交换是一组原子操作,因为是硬件级别的操作,所以效率会高一些。

CAS 最常见和基础的使用地方在 java.util.concurrent.atomic 包下面,譬如 AtomicInteger 使用 CAS 的实质如下(基于JDK 1.8之前,1.8开始已经再次优化了,看不见阻塞的逻辑了):

package java.util.concurrent.atomic;import java.util.function.IntUnaryOperator;import java.util.function.IntBinaryOperator;import sun.misc.Unsafe;public class AtomicInteger extends Number implements java.io.Serializable {    private static final long serialVersionUID = 6214790243416807050L;    // setup to use Unsafe.compareAndSwapInt for updates    private static final Unsafe unsafe = Unsafe.getUnsafe();    private static final long valueOffset;    static {        try {            valueOffset = unsafe.objectFieldOffset                (AtomicInteger.class.getDeclaredField("value"));        } catch (Exception ex) { throw new Error(ex); }    }    private volatile int value;    /**     * Creates a new AtomicInteger with the given initial value.     *     * @param initialValue the initial value     */    public AtomicInteger(int initialValue) {        value = initialValue;    }    /**     * Creates a new AtomicInteger with initial value {@code 0}.     */    public AtomicInteger() {    }    /**     * Gets the current value.     *     * @return the current value     */    public final int get() {        return value;    }    /**     * Sets to the given value.     *     * @param newValue the new value     */    public final void set(int newValue) {        value = newValue;    }    /**     * Eventually sets to the given value.     *     * @param newValue the new value     * @since 1.6     */    public final void lazySet(int newValue) {        unsafe.putOrderedInt(this, valueOffset, newValue);    }    /**     * Atomically sets to the given value and returns the old value.     *     * @param newValue the new value     * @return the previous value     */    public final int getAndSet(int newValue) {        return unsafe.getAndSetInt(this, valueOffset, newValue);    }    /**     * Atomically sets the value to the given updated value     * if the current value {@code ==} the expected value.     *     * @param expect the expected value     * @param update the new value     * @return {@code true} if successful. False return indicates that     * the actual value was not equal to the expected value.     */    public final boolean compareAndSet(int expect, int update) {        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);    }    /**     * Atomically sets the value to the given updated value     * if the current value {@code ==} the expected value.     *     * 

May fail * spuriously and does not provide ordering guarantees, so is * only rarely an appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return {@code true} if successful */ public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } /** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } /** * Atomically decrements by one the current value. * * @return the previous value */ public final int getAndDecrement() { return unsafe.getAndAddInt(this, valueOffset, -1); } /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the previous value */ public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); } /** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } /** * Atomically decrements by one the current value. * * @return the updated value */ public final int decrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, -1) - 1; } /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the updated value */ public final int addAndGet(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta) + delta; } /** * Atomically updates the current value with the results of * applying the given function, returning the previous value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the previous value * @since 1.8 */ public final int getAndUpdate(IntUnaryOperator updateFunction) { int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return prev; } /** * Atomically updates the current value with the results of * applying the given function, returning the updated value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the updated value * @since 1.8 */ public final int updateAndGet(IntUnaryOperator updateFunction) { int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return next; } /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the previous value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the previous value * @since 1.8 */ public final int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) { int prev, next; do { prev = get(); next = accumulatorFunction.applyAsInt(prev, x); } while (!compareAndSet(prev, next)); return prev; } /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the updated value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the updated value * @since 1.8 */ public final int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) { int prev, next; do { prev = get(); next = accumulatorFunction.applyAsInt(prev, x); } while (!compareAndSet(prev, next)); return next; } /** * Returns the String representation of the current value. * @return the String representation of the current value */ public String toString() { return Integer.toString(get()); } /** * Returns the value of this {@code AtomicInteger} as an {@code int}. */ public int intValue() { return get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code long} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public long longValue() { return (long)get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code float} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { return (float)get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code double} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); }}

可以发现基于 CAS 可以实现乐观的非阻塞算法的 Java atomic 原子变量,除此之外还可以实现悲观阻塞式算法,譬如锁机制等。

CAS 策略算法其实有个致命的 ABA 问题,如果一个变量 V 初次读取时值为 A,并且在准备赋值时检查到仍然是 A,而这时候又在多线程的场景下我们是无法确认这段时间之内是否值被其他线程先修改为 B 接着改回了 A,所以 CAS 就会在这种场景下误认为变量从来没被修改过,也就是 ABA 问题了,这个问题一般是没啥影响的,如果程序逻辑需要处理 ABA 场景就可以使用 AtomicStampedReference,在修改值的同时传入一个唯一标记(譬如时间戳),只有值和标记都想等才进行修改,使用 AtomicMarkableReference 也可以,只不过标记是 boolean 类型。

转载地址:http://dvbai.baihongyu.com/

你可能感兴趣的文章
SuperMap iClient3D for WebGL教程-CallbackProperty
查看>>
如何修改leaflet聚合图的层级和样式
查看>>
三维分析之开敞度分析
查看>>
BIM+GIS应用的八大挑战
查看>>
.net实现.aspx页面自动加载.cs程序定义的变量并按照格式输出
查看>>
[Leetcode]最后一个单词的长度
查看>>
merges sort use c++
查看>>
插入排序用递归实现
查看>>
工作流审批平台-审批流程-指定审批部门
查看>>
商务智能-系统概述-数据图形方式
查看>>
软件项目管理系统-项目管理-模块定义-开发内容
查看>>
工作流审批平台-审批功能
查看>>
商务智能-基本方法-特征与角度
查看>>
软件项目管理系统-项目管理-模块定义-开发笔记
查看>>
工作流审批平台-业务申请-申请书一览
查看>>
商务智能-基本方法-数据钻取
查看>>
C++程序员技术需求规划(发展方向)
查看>>
聊聊我当年在培训学校做开发的经历
查看>>
用Docker搭建Redis主从复制的集群
查看>>
盘点这些年我出的书,以及由此得到的收获
查看>>