忻州SEO摘要

CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS。

 

了解CAS(Compare-And-Swap)

CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS。

JAVA中CAS的实现

JAVA中的cas主要使用的是Unsafe方法,Unsafe的CAS操作主要是基于硬件平台的汇编指令,目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。

Unsafe提供了三个方法用于CAS操作,分别是

1
<p style="line-height: 1.5em;">public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);<br>public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);<br>public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);<br></p>
  • value 表示 需要操作的对象

  • valueOffset 表示 对象(value)的地址的偏移量(通过Unsafe.objectFieldOffset(Field valueField)获取)

  • expect 表示更新时value的期待值

  • update 表示将要更新的值

具体过程为每次在执行CAS操作时,线程会根据valueOffset去内存中获取当前值去跟expect的值做对比如果一致则修改并返回true,如果不一致说明有别的线程也在修改此对象的值,则返回false

Unsafe类中compareAndSwapInt的具体实现:

1
<p style="line-height: 1.5em;">UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))<br>  UnsafeWrapper("Unsafe_CompareAndSwapInt");<br>  oop p = JNIHandles::resolve(obj);<br>  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;<br>UNSAFE_END<br></p>

ABA问题

线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。

aba.png

例子

1
<p style="line-height: 1.5em;">public static AtomicInteger a = new AtomicInteger(1);public static void main(String[] args){<br>    Thread main = new Thread(() -> {<br>        System.out.println("操作线程" + Thread.currentThread() +",初始值 = " + a);  //定义变量 a = 1<br>        try {<br>            Thread.sleep(1000);  //等待1秒 ,以便让干扰线程执行<br>        } catch (InterruptedException e) {<br>            e.printStackTrace();<br>        }        boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作<br>        System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);<br>    },"主操作线程");<br><br>    Thread other = new Thread(() -> {<br>        Thread.yield();  //确保thread-main线程优先执行<br>        a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2<br>        System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ a);<br>        a.decrementAndGet(); // a 减 1, a - 1 = 2 - 1 = 1<br>        System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);<br>    },"干扰线程");<br><br>    main.start();<br>    other.start();<br>}<br></p>

输出> 操作线程Thread[主操作线程,5,main],初始值 = 1>
操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2>
操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1>
操作线程Thread[主操作线程,5,main],CAS操作结果: true

解决ABA方案

思路

解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。

aba_2.png

JAVA中ABA中解决方案(AtomicStampedReference)

AtomicStampedReference主要维护包含一个对象引用以及一个可以自动更新的整数"stamp"的pair对象来解决ABA问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//关键代码
public class AtomicStampedReference<V> {
    private static class Pair<T> {
        final T reference;  //维护对象引用
        final int stamp;  //用于标志版本
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }
    private volatile Pair<V> pair;
    ....
    /**
      * expectedReference :更新之前的原始值
      * newReference : 将要更新的新值
      * expectedStamp : 期待更新的标志版本
      * newStamp : 将要更新的标志版本
      */
    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair; //获取当前pair
        return
            expectedReference == current.reference && //原始值等于当前pair的值引用,说明值未变化
            expectedStamp == current.stamp && // 原始标记版本等于当前pair的标记版本,说明标记未变化
            ((newReference == current.reference &&
              newStamp == current.stamp) || // 将要更新的值和标记都没有变化
             casPair(current, Pair.of(newReference, newStamp))); // cas 更新pair
    }
}

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static AtomicStampedReference<Integer> atomicStampedRef =
        new AtomicStampedReference<>(10);
public static void main(String[] args){
    Thread main = new Thread(() -> {
        System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference());
        int stamp = atomicStampedRef.getStamp(); //获取当前标识别
        try {
            Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1);  //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
        System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
    },"主操作线程");
    Thread other = new Thread(() -> {
        Thread.yield(); // 确保thread-main 优先执行
atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
        System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
        atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
        System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
    },"干扰线程");
    main.start();
    other.start();
}

// 输出

> 操作线程Thread[主操作线程,5,main],初始值 a = 2

> 操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2

> 操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1

> 操作线程Thread[主操作线程,5,main],CAS操作结果: false

来源:内蒙古SEO

Java中CAS-ABA的问题解决方案的更多相关文章

  1. 计算价格, java中浮点数精度丢失的解决方案

    计算价格, java中浮点数精度丢失的解决方案

  2. Java中CAS原理分析(volatile和synchronized浅析)

    CAS是什么? CAS英文解释是比较和交换,是cpu底层的源语,是解决共享变量原子性实现方案,它定义了三个变量,内存地址值对应V,期待值E和要修改的值U,如下图所示,这些变量都是在高速缓存中的,如果两 ...

  3. Java中CAS原理详解

    在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...

  4. Java中CAS详解

    在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...

  5. CAS -- ABA问题的解决方案

    我们现在来说什么是ABA问题.假设内存中有一个值为A的变量,存储在地址V中. 此时有三个线程想使用CAS的方式更新这个变量的值,每个线程的执行时间有略微偏差.线程1和线程2已经获取当前值,线程3还未获 ...

  6. Java中CAS 基本实现原理

    一.前言 了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.a ...

  7. Java中CAS 基本实现原理 和 AQS 原理

    一.前言了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.at ...

  8. Java中各类Cache机制实现解决方案[来自CSDN]

    摘要:在Java中,不同的类都有自己单独的Cache机制,实现的方法也可能有所不同,文章列举了Java中常见的各类Cache机制的实现方法,同时进行了综合的比较. 在Java中,不同的类都有自己单独的 ...

  9. Java 中 CAS

    一.CAS 概念 CAS ,全称 Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制. 实现思想 CAS(V.A.B) ,V为内存地址,A为预期原值,B ...

  10. 详解java中CAS机制所导致的问题以及解决——内存顺序冲突

    [CAS机制] 指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一 ...

随机推荐

  1. [转帖]商用数据库之死:Oracle 面临困境

    商用数据库之死:Oracle 面临困境 投递人 itwriter 发布于 2019-10-20 08:22 评论(1) 有238人阅读 原文链接 [收藏] « » https://news.cnblo ...

  2. [转帖]时序数据库技术体系 – InfluxDB TSM存储引擎之数据读取

    时序数据库技术体系 – InfluxDB TSM存储引擎之数据读取 http://hbasefly.com/2018/05/02/timeseries-database-7/  2018年5月2日   ...

  3. tp5 关键字模糊查询 日期查询 小于大于某范围等查询的优点

    挺不错,用熟了这tp5封装的很方便. 类似上边一个查询多个操作,基本在model 一个方法搞定代码也不用很多, 首先要学会用scope  网上搜tp scope 有几个例子可以借鉴 model 内添加 ...

  4. php扩展安装方式

    目录 扩展安装 php源码编译安装 PEAR.PECL phpize 源码安装 扩展安装 以php安装swoole扩展为例. php源码编译安装 下载源码包并编译安装 $ wget http://cn ...

  5. Struts笔记4

    Struts2-拦截器-单个拦截器 自定义拦截器 1.创建一个继承AbstractInterceptor的类 package com.gyf.web.interceptor; import com.o ...

  6. 利用Python进行数据分析 第8章 数据规整:聚合、合并和重塑.md

    学习时间:2019/11/03 周日晚上23点半开始,计划1110学完 学习目标:Page218-249,共32页:目标6天学完(按每页20min.每天1小时/每天3页,需10天) 实际反馈:实际XX ...

  7. Spring AOP日志实现(四)--Bean的设计

    日志Bean的设计: 类名及方法名:

  8. Dockerfile编写,以及设置一个自启动脚本

    FROM:指定基础镜像,必须为第一个命令 MAINTAINER: 维护者信息 RUN:构建镜像时执行的命令 ADD:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问 ...

  9. codeforce 849D. Make a Permutation!

    D. Make a Permutation! time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  10. python之(urllib、urllib2、lxml、Selenium+PhantomJS)爬虫

    一.最近在学习网络爬虫的东西,说实话,没有怎么写过爬虫,Java里面使用的爬虫也没有怎么用过.这里主要是学习Python的时候,了解到Python爬虫的强大,和代码的简介,这里会简单的从入门看是说起, ...