以AtomicLong的compareAndSet方法举例。先说结论:如果CPU支持,则基于CPU指令(CMPXCHG8)实现;否则使用ObjectLocker锁实现。

分析过程如下:

该方法在jdk中源代码如下:

public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

unsafe是sun.misc.Unsafe的一个实例,Unsafe类在jdk中没有源代码,是由jvm提供的native代码。在openjdk中对应位置是hotspot/src/share/vm/prims/unsafe.cpp

jdk代码里没有用锁,对用户来说是无锁的操作

openjdk里是怎么实现unsafe.compareAndSwapLong的呢?直接用代码说话,如下:

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
UnsafeWrapper("Unsafe_CompareAndSwapLong");
Handle p (THREAD, JNIHandles::resolve(obj));
jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
if (VM_Version::supports_cx8())
return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
else {
jboolean success = false;
ObjectLocker ol(p, THREAD);
if (*addr == e) { *addr = x; success = true; }
return success;
}
UNSAFE_END

可以看到,如果不支持cx8,那么就需要用到ObjectLocker锁,那么什么 VM_Version::supports_cx8() 的底层实现又是什么呢?还是上代码,在openjdk/hotspot/src/share/vm/runtime/vm_version.hpp里

static bool supports_cx8()  {
#ifdef SUPPORTS_NATIVE_CX8
return true;
#else
return _supports_cx8;
#endif
}
_supports_cx8在何处赋值呢?该值默认为false,在x86系统中使用supports_cmpxchg8()方法赋值,在sparc系统中使用has_v9()赋值。我们来看一下x86系统中的情况,
static bool supports_cmpxchg8() { return (_cpuFeatures & CPU_CX8) != ; }

_cpuFeatures定义如下:

static int _cpuFeatures;     // features returned by the "cpuid" instruction
// 0 if this instruction is not available

CPU_CX8定义如下:

enum {
CPU_CX8 = ( << ), // next bits are from cpuid 1 (EDX)
CPU_CMOV = ( << ),
CPU_FXSR = ( << ),
CPU_HT = ( << ),
CPU_MMX = ( << ),
CPU_3DNOW_PREFETCH = ( << ), // Processor supports 3dnow prefetch and prefetchw instructions
// may not necessarily support other 3dnow instructions
CPU_SSE = ( << ),
CPU_SSE2 = ( << ),
CPU_SSE3 = ( << ), // SSE3 comes from cpuid 1 (ECX)
CPU_SSSE3 = ( << ),
CPU_SSE4A = ( << ),
CPU_SSE4_1 = ( << ),
CPU_SSE4_2 = ( << ),
CPU_POPCNT = ( << ),
CPU_LZCNT = ( << ),
CPU_TSC = ( << ),
CPU_TSCINV = ( << ),
CPU_AVX = ( << ),
CPU_AVX2 = ( << ),
CPU_AES = ( << ),
CPU_ERMS = ( << ), // enhanced 'rep movsb/stosb' instructions
CPU_CLMUL = ( << ) // carryless multiply for CRC
} cpuFeatureFlags;

在刨根问底_cpuFeatures的值是怎么来的?

_cpuFeatures = feature_flags();
static uint32_t feature_flags() {
uint32_t result = ;
if (_cpuid_info.std_cpuid1_edx.bits.cmpxchg8 != )
result |= CPU_CX8;
if (_cpuid_info.std_cpuid1_edx.bits.cmov != )
result |= CPU_CMOV;
if (_cpuid_info.std_cpuid1_edx.bits.fxsr != || (is_amd() &&
_cpuid_info.ext_cpuid1_edx.bits.fxsr != ))
result |= CPU_FXSR;
// HT flag is set for multi-core processors also.
if (threads_per_core() > )
result |= CPU_HT;
if (_cpuid_info.std_cpuid1_edx.bits.mmx != || (is_amd() &&
_cpuid_info.ext_cpuid1_edx.bits.mmx != ))
result |= CPU_MMX;
if (_cpuid_info.std_cpuid1_edx.bits.sse != )
result |= CPU_SSE;
if (_cpuid_info.std_cpuid1_edx.bits.sse2 != )
result |= CPU_SSE2;
if (_cpuid_info.std_cpuid1_ecx.bits.sse3 != )
result |= CPU_SSE3;
if (_cpuid_info.std_cpuid1_ecx.bits.ssse3 != )
result |= CPU_SSSE3;
if (_cpuid_info.std_cpuid1_ecx.bits.sse4_1 != )
result |= CPU_SSE4_1;
if (_cpuid_info.std_cpuid1_ecx.bits.sse4_2 != )
result |= CPU_SSE4_2;
if (_cpuid_info.std_cpuid1_ecx.bits.popcnt != )
result |= CPU_POPCNT;
if (_cpuid_info.std_cpuid1_ecx.bits.avx != &&
_cpuid_info.std_cpuid1_ecx.bits.osxsave != &&
_cpuid_info.xem_xcr0_eax.bits.sse != &&
_cpuid_info.xem_xcr0_eax.bits.ymm != ) {
result |= CPU_AVX;
if (_cpuid_info.sef_cpuid7_ebx.bits.avx2 != )
result |= CPU_AVX2;
}
if (_cpuid_info.std_cpuid1_edx.bits.tsc != )
result |= CPU_TSC;
if (_cpuid_info.ext_cpuid7_edx.bits.tsc_invariance != )
result |= CPU_TSCINV;
if (_cpuid_info.std_cpuid1_ecx.bits.aes != )
result |= CPU_AES;
if (_cpuid_info.sef_cpuid7_ebx.bits.erms != )
result |= CPU_ERMS;
if (_cpuid_info.std_cpuid1_ecx.bits.clmul != )
result |= CPU_CLMUL; // AMD features.
if (is_amd()) {
if ((_cpuid_info.ext_cpuid1_edx.bits.tdnow != ) ||
(_cpuid_info.ext_cpuid1_ecx.bits.prefetchw != ))
result |= CPU_3DNOW_PREFETCH;
if (_cpuid_info.ext_cpuid1_ecx.bits.lzcnt != )
result |= CPU_LZCNT;
if (_cpuid_info.ext_cpuid1_ecx.bits.sse4a != )
result |= CPU_SSE4A;
} return result;
}
至此,基本可以断定这里的判断,是从CPUID中获取的信息,来看CPU是否支持CMPXCHG8指令。

再回过头来看这句:
return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;

这里Atomic::cmpxchg方法是核心,定义在openjdk/hotspot/src/share/vm/runtime/atomic.hpp

inline static jlong    cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value);

在不同系统中有不同的实现,在linux_x86中:openjdk/hotspot/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp

inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
bool mp = os::is_MP();
__asm__ __volatile__ (LOCK_IF_MP(%) "cmpxchgq %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}

在windows_x86中:openjdk/hotspot/os_cpu/linux_x86/vm/atomic_windows_x86.inline.hpp

inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
int mp = os::is_MP();
jint ex_lo = (jint)exchange_value;
jint ex_hi = *( ((jint*)&exchange_value) + );
jint cmp_lo = (jint)compare_value;
jint cmp_hi = *( ((jint*)&compare_value) + );
__asm {
push ebx
push edi
mov eax, cmp_lo
mov edx, cmp_hi
mov edi, dest
mov ebx, ex_lo
mov ecx, ex_hi
LOCK_IF_MP(mp)
cmpxchg8b qword ptr [edi]
pop edi
pop ebx
}
}

可以看出,当CPU支持时,最终确实是直接用cmpxchg相关指令实现的。

java Atomic compareAndSet部分原理分析的更多相关文章

  1. JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balaba ...

  2. Java NIO使用及原理分析 (四)

    在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...

  3. (6)Java数据结构-- 转:JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析  http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balab ...

  4. Java NIO使用及原理分析 (四)(转)

    在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...

  5. Java NIO使用及原理分析(1-4)(转)

    转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...

  6. Java NIO使用及原理分析(二)

    在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...

  7. Java NIO使用及原理分析(二)(转)

    在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...

  8. Java NIO使用及原理分析 (一)(转)

    最近由于工作关系要做一些Java方面的开发,其中最重要的一块就是Java NIO(New I/O),尽管很早以前了解过一些,但并没有认真去看过它的实现原理,也没有机会在工作中使用,这次也好重新研究一下 ...

  9. 支付宝app支付java后台流程、原理分析(含nei wang chuan tou)

    java版支付宝app支付流程及原理分析 本实例是基于springmvc框架编写     一.流程步骤         1.执行流程           当手机端app(就是你公司开发的app)在支付 ...

随机推荐

  1. SyntaxError: Non-UTF-8 code starting with '\xc1'

    问题描述: 运行Python脚本,报错了:SyntaxError: Non-UTF-8 code starting with '\xc1' 不是不支持中文,是脚本的编码格式设置不正确. 在python ...

  2. json data转匿名对象C#

    using Newtonsoft.Json.Linq; 代码如下: static void Main(string[] args) { Console.WriteLine("Test 4.8 ...

  3. 通过JS下载 or 唤起App

    判断唤起app的连接是否在WeChat中打开 let isWeiXin = navigator.userAgent.toLowerCase().indexOf('micromessenger') != ...

  4. json工具类(二)——google包

    package com.ruoyi.common.utils.json; import java.util.List; import com.google.gson.Gson; import com. ...

  5. uni-app如何解决在for循环里调用异步请求获取数据顺序混乱问题?

    总结/朱季谦 先前有一次做uni-app的js接口对接时,遇到过这样的情况,在for循环里,调用一个异步请求时,返回来的值顺序是乱的,因此,在以下的代码里,push到数组里的值,每次的顺序可能都是不一 ...

  6. USE11 上oracle11导入数据中文乱码

    分类专栏: 数据库 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/suqimm/artic ...

  7. Dozer JAVA的POJO 映射工具

    Dozerhttp://www.manongjc.com/article/50949.html JAVA的映射工具 BeanUtils dozer的使用方法https://blog.csdn.net/ ...

  8. WebAPI HelpPage帮助页

    WebAPI HelpPage是个插件,根据代码的注释生成API说明页,一目了然. 下面开始安装和配置 1.添加引用 先选择管理NuGet程序包,搜索 Microsoft.AspNet.WebApi. ...

  9. 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌(示例代码)

    在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Ap ...

  10. asp.net oracle 中文乱码 解决方法

    asp.net oracle 中文乱码 解决方法 不需要去修改oracle以及client的字符集,只需要在web.config中连接字符串上加上 Unicode=True 问题解决. <add ...