首页 > 编程语言 >java中乐观锁CAS的实现探究——AtomicInteger

java中乐观锁CAS的实现探究——AtomicInteger

时间:2022-11-27 00:55:16浏览次数:47  
标签:java CAS cmpxchg value jint 指令 AtomicInteger offset cpu

CAS

CAS——compare and swap,比较并交换。是一种乐观锁的实现方式。更新操作时,在每次更新之前,都先比较一下被更新的目标T,值是不是等于读取到的旧值O,如果相等,则认为在读取到并运算出新值期间没有被其他线程访问,将值更新为新值N,如果T ≠ O,则期间被修改,重新进行【读取 --> 运算 --> 比较】的过程,直到能成功的更新。

乐观锁:乐观的认为不会有其他线程对此资源进行并发访问的,不锁定资源的方式来更新资源。典型的实现CAS。

悲观锁:悲观的认为一定会有其他线程对此资源进行并发访问,一定要锁定资源,自己访问的时候其他线程不允许访问。典型的如synchronized。

java中就有典型的CAS实现,比如AtomicInteger类的源码。

我们可以看看 java.util.concurrent.atomic.AtomicInteger#incrementAndGet 方法:

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

可以发现,自增方法的实现是调用了Unsafe类中的实现。
那我们再看sun.misc.Unsafe相关代码:

/**
 * Atomically adds the given value to the current value of a field
 * or array element within the given object <code>o</code>
 * at the given <code>offset</code>.
 *
 * @param o object/array to update the field/element in
 * @param offset field/element offset
 * @param delta the value to add
 * @return the previous value
 * @since 1.8
 */
public final int getAndAddInt(Object o, long offset, int delta) {
  int v;
  do {
    v = getIntVolatile(o, offset);
  } while (!compareAndSwapInt(o, offset, v, v + delta));
  return v;
}  


/**
 * Atomically update Java variable to <tt>x</tt> if it is currently
 * holding <tt>expected</tt>.
 * @return <tt>true</tt> if successful
 */
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);

实际的逻辑实现,最终是调用到了一个native方法——compareAndSwapInt(),此方法由JVM使用C++实现。

按照一般认知,比较并交换,包含多个步骤(取值,比较,交换),而这多个步骤放在一起做,不是原子的,那么它是如何保证它的原子性的?

看方法的描述:Atomically update Java variable to x if it is currently holding expected. —— 如果变量的值是预期的,则原子的更新为x。

可以看到,整个比较并交换过程,是由JVM底层保证的原子性。

那么JVM底层又是如何保证原子性的呢?

在jvm源码中,找到对应方法的实现逻辑,对应源码在 jdk8u : unsafe.cpp文件中,代码如下:

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);
		jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
		return (jint)(atomic::cmpxchg(x, addr ,e)) == e;
UNSAFE_END

我们看到,他的最后返回Atomic::cmpxchg调用,cmpxchg的含义就为 compare and exchange。

再进一步跟源码,在 jdk8u: atomic_linux_x86.inline.hpp的93行, atomic_linux_x86.inline.hpp这个文件,对应的就是atomic这个类,在linux_x86架构上实现。

inline jint Atomic::cmpxchg(jint exchange_value,volatile jint* dest, ... ...){
  int mp = os::is_MP();
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchg %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), r(mp)
                    : "cc", "memory");
  return exchange_value;
  );
}

os::is_MP(),返回当前系统是否是多核。

cmpxchg本身也是一条CPU指令,但也不是原子的。

__asm__表示后面的是一条汇编指令。LOCK_IF_MP是一个宏,表示如果是多核cup,后面的指令需要lock一下,加上lock以后,其他核的cpu就无法打断后面的指令,也就是原子的了。所以多核cpu会有指令:lock cmpxchg

这个lock指令,是cpu级别的锁,但不是总线锁,而是在执行lock后的指令前,锁定一个北桥信号。(关于这一段描述,是摘抄自网上其他内容的说法,涉及到计算机原理和cpu指令相关的知识)

总结:JVM的AtomicInteger 的CAS实现,实际上是汇编代码调用cpu的cmpxchg指令,且该条cpu指令并非原子,由汇编代码判断是否对核cpu,多核则该指令进行加锁保证其原子性。

标签:java,CAS,cmpxchg,value,jint,指令,AtomicInteger,offset,cpu
From: https://www.cnblogs.com/kingdy/p/cas.html

相关文章

  • JAVA网络编程TCP实现聊天功能,附在IDEA中同时运行2个或以上相同的java程序
    在IDEA中同时运行2个或以上相同的java程序在日常编写测试代码时,有时候会需要在idea上同时运行两个及以上相同的java程序,如:想运行两个CLIENTLOGIN测试聊天室效果。1.点击E......
  • java9
    Java9新特性Java9发布于2017年9月22日,带来了很多新特性,其中最主要的变化是已经实现的模块化系统。接下来我们会详细介绍Java9的新特性。Java9新特性模块系......
  • 基于java+swing的图书管理系统
    功能展示登录管理员-主界面管理员-增加书籍管理员-更新和删除书籍管理员-查找书籍管理员-查找所有书籍管理员-添加用户管理员-更新和删除用户管理员-查找......
  • Javascript(笔记53) - promise - 3 几个关键问题
    如何改变Promise的状态?1)resolve(value):如果当前是pending 就会变为resolved(或fulfilled); 2)reject(reason):如果当前是pending 就会变为rejected; 3) 抛出异常:如......
  • java中abstract详解
     Abstract(抽象)可以修饰类、方法 如果将一个类设置为abstract,则此类必须被继承使用。此类不可生成对象,必须被继承使用。 Abstract可以将子类的共性最大限度的抽取出来,放......
  • Java基础——继承(Extends)
    使用extends(继承)有什么好处?使用继承可以实现代码的重用,通过继承,子类可以继承父类的成员变量及成员方法,同时子类也可以定义自己的成员变量和成员方法。届时,子类将具有父类......
  • java——@ApiOperation注解
    @RequestMapping(value="/add",method=RequestMethod.POST)@ApiOperation(value="添加用户")publicsynchronizedBaseResponse<Long>addUser(@RequestBody......
  • javascript 执行上下文
    <script>//执行上下文,顺序执行到此出会产生一个全局的执行上下文(ECG),并把全局ECG放到ECS(执行上下文栈)中//VO:GO(globanobject)对浏览器而言,window即......
  • Java控制台输出进度条
    源码packagecom.yang.restdemo.util;importjava.util.concurrent.TimeUnit;importjava.util.stream.Stream;/***@description:进度条*@author:YangJia......
  • 用JStack和Top分析Java进程CPU占用率
    在知道哪个Java进程CPU占用率过高以后:1.使用命令jstackPID命令打印出CPU占用过高进程的线程栈,例如jstack12012>12012.txt2.使用top-H-pPID命令查看对应进程是哪......