CAS原理详解_外燃机工作原理

CAS原理详解_外燃机工作原理CAS简介CAS的意思是compareandswap,比较并交换。CAS的引入是为了解决java锁机制带来的性能问题。锁机制存在以下问题:(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。解决线程安全问题volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。独占锁

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

CAS简介

CAS 的意思是 compare and swap,比较并交换。CAS 的引入是为了解决java锁机制带来的性能问题。锁机制存在以下问题:

(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。

(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。

解决线程安全问题volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。
独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

CAS原理流程图

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
在这里插入图片描述

比如一个很简单的操作,把变量 A = 2 加 1,结果为 3.

则先读取 A 的当前值 E 为 2,在内存计算结果 V 为 3,比较之前读出来的 A 的当前值 2 和 最新值,如果最新值为 2 ,表示这个值没有被别人改过,则放心的把最终的值更新为 3.

ABA 问题

有一种情况是,在你更新结果之前,其他有个线程在中途把 A 更新成了 5 ,又更新回了 2。但是在当前线程看起来,没有被改过。

JAVA CAS实现原理

JAVA中的CAS是通过调用JNI(JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言)的代码实现的。

以JAVA中的AtomicInteger的compareAndSet为例:

public class AtomicInteger extends Number implements java.io.Serializable {

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            // 计算变量 value 在类对象中的偏移
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    
    public final boolean compareAndSet(int expect, int update) {
        /*
         * compareAndSet 实际上只是一个壳子,主要的逻辑封装在 Unsafe 的 
         * compareAndSwapInt 方法中
         */
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    
    // ......
}

public final class Unsafe {
    // compareAndSwapInt 是 native 类型的方法,继续往下看
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);
    // ......
}

compareAndSet实际上只是一个壳子,主要的逻辑封装在Unsafe的compareAndSwapInt方法中
对该方法进行分析:

参数名称 含义
o 需要修改的对象
offset 需要修改的字段到对象头的偏移量(通过偏移量,可以快速定位修改的是哪个字段)
expected 期望值
x 要设置的值

C++源码

// unsafe.cpp
/*
 * 这个看起来好像不像一个函数,不过不用担心,不是重点。UNSAFE_ENTRY 和 UNSAFE_END 都是宏,
 * 在预编译期间会被替换成真正的代码。下面的 jboolean、jlong 和 jint 等是一些类型定义(typedef):
 * 
 * jni.h
 *     typedef unsigned char   jboolean;
 *     typedef unsigned short  jchar;
 *     typedef short           jshort;
 *     typedef float           jfloat;
 *     typedef double          jdouble;
 * 
 * jni_md.h
 *     typedef int jint;
 *     #ifdef _LP64 // 64-bit
 *     typedef long jlong;
 *     #else
 *     typedef long long jlong;
 *     #endif
 *     typedef signed char jbyte;
 */
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  // 根据偏移量,计算 value 的地址。这里的 offset 就是 AtomaicInteger 中的 valueOffset
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  // 调用 Atomic 中的函数 cmpxchg,该函数声明于 Atomic.hpp 中
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

// atomic.cpp
unsigned Atomic::cmpxchg(unsigned int exchange_value,
                         volatile unsigned int* dest, unsigned int compare_value) {
  assert(sizeof(unsigned int) == sizeof(jint), "more work to do");
  /*
   * 根据操作系统类型调用不同平台下的重载函数,这个在预编译期间编译器会决定调用哪个平台下的重载
   * 函数。相关的预编译逻辑如下:
   * 
   * atomic.inline.hpp:
   *    #include "runtime/atomic.hpp"
   *    
   *    // Linux
   *    #ifdef TARGET_OS_ARCH_linux_x86
   *    # include "atomic_linux_x86.inline.hpp"
   *    #endif
   *   
   *    // 省略部分代码
   *    
   *    // Windows
   *    #ifdef TARGET_OS_ARCH_windows_x86
   *    # include "atomic_windows_x86.inline.hpp"
   *    #endif
   *    
   *    // BSD
   *    #ifdef TARGET_OS_ARCH_bsd_x86
   *    # include "atomic_bsd_x86.inline.hpp"
   *    #endif
   * 
   * 接下来分析 atomic_windows_x86.inline.hpp 中的 cmpxchg 函数实现
   */
  return (unsigned int)Atomic::cmpxchg((jint)exchange_value, (volatile jint*)dest,
                                       (jint)compare_value);
}

先想办法拿到变量value在内存中的地址。
通过Atomic::cmpxchg实现比较替换,其中参数x是即将更新的值,参数e是原内存的值。

concurrent包的实现

由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式:

A线程写volatile变量,随后B线程读这个volatile变量。
A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。

Java的CAS会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读-改-写操作,这是在多处理器中实现同步的关键(从本质上来说,能够支持原子性读-改-写指令的计算机器,是顺序计算图灵机的异步等价机器,因此任何现代的多处理器都会去支持某种能对内存执行原子性读-改-写操作的原子指令)。同时,volatile变量的读/写和CAS可以实现线程之间的通信。把这些特性整合在一起,就形成了整个concurrent包得以实现的基石。如果我们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式:

首先,声明共享变量为volatile;
然后,使用CAS的原子条件更新来实现线程之间的同步;
同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。

AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类来实现的。从整体来看,concurrent包的实现示意图如下:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/181350.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • phpstorm2021.2激活码破解方法

    phpstorm2021.2激活码破解方法,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月14日
    219
  • 关于UDP接收icmp端口不可达(port unreachable)

    关于UDP接收icmp端口不可达(port unreachable)本篇分为3部分1:报文格式2:产生的原因3:linux协议栈如何处理4:应用层如何获取1:报文如下,10.30.13.1往10.30.16.10的80端口发送了一个UDP报文,80端口其实监听的是TCP。服务器回复了一个类型为端口不可达的ICMP,ICMP数据部分就是请求UDPip层及其以上的数据。2:原因首先原因就是接收udp报文…

    2022年7月12日
    12
  • 跨域访问是什么_什么是跨域?怎么解决跨域问题

    跨域访问是什么_什么是跨域?怎么解决跨域问题跨域访问是什么同源策略1995年,同源政策由Netscape公司引人浏览器。目前,所有测览器都实行这个政策。同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。随着互联网的发展,“同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制:】Cookie、LocalStorage和IndexDB无法读取DOM无法获得AJAX请求不能发送虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。所谓“同源”指的是“三个相同”:协议相同、域名相同以及端相同。域名是什么

    2022年10月1日
    0
  • java getclassloader_关于getClass(),Object.class,getClassLoader的理解

    java getclassloader_关于getClass(),Object.class,getClassLoader的理解1、对Class类的理解:Class类包含了类的信息,如构造方法、方法、属性,可用于反射。以下是所有方法2、获取Class类对象的几种方法:Testtest=newTest();(1).test.getClass();在运行时确定,所以运行实例才是该类对象。super.getClass()不能获得父类的类对象,仍然是当前类对象。获得父类类对象:test.getClass().getSup…

    2022年6月11日
    20
  • Oracle 904_oracle01017

    Oracle 904_oracle01017SQL>desceq_admin.its_earthquake_recover;名前NULL?型—————————————————————————–ORG_INF…

    2022年9月20日
    0
  • 运算放大器工作原理

    运算放大器工作原理运算放大器工作原理作者:何富和时间:2015-03-22来源:电子产品世界  运算放大器基本上可以算得上是模拟电路的基本需要了解的电路之一,而要想更好用好运放,透彻地了解运算放大器工作原理是无可避免,但是运放攻略太多,那不妨来试试这篇用电路图作为主线的文章来带你领略运算放大器的工作原理吧。本文引用地址:http://www.eepw.com.cn/article/271351.htm1.运算放

    2022年4月28日
    48

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号