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

分析过程如下:

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

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

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

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

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

  1. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  2. UnsafeWrapper("Unsafe_CompareAndSwapLong");
  3. Handle p (THREAD, JNIHandles::resolve(obj));
  4. jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  5. if (VM_Version::supports_cx8())
  6. return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
  7. else {
  8. jboolean success = false;
  9. ObjectLocker ol(p, THREAD);
  10. if (*addr == e) { *addr = x; success = true; }
  11. return success;
  12. }
  13. UNSAFE_END

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

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

_cpuFeatures定义如下:

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

CPU_CX8定义如下:

  1. enum {
  2. CPU_CX8 = ( << ), // next bits are from cpuid 1 (EDX)
  3. CPU_CMOV = ( << ),
  4. CPU_FXSR = ( << ),
  5. CPU_HT = ( << ),
  6. CPU_MMX = ( << ),
  7. CPU_3DNOW_PREFETCH = ( << ), // Processor supports 3dnow prefetch and prefetchw instructions
  8. // may not necessarily support other 3dnow instructions
  9. CPU_SSE = ( << ),
  10. CPU_SSE2 = ( << ),
  11. CPU_SSE3 = ( << ), // SSE3 comes from cpuid 1 (ECX)
  12. CPU_SSSE3 = ( << ),
  13. CPU_SSE4A = ( << ),
  14. CPU_SSE4_1 = ( << ),
  15. CPU_SSE4_2 = ( << ),
  16. CPU_POPCNT = ( << ),
  17. CPU_LZCNT = ( << ),
  18. CPU_TSC = ( << ),
  19. CPU_TSCINV = ( << ),
  20. CPU_AVX = ( << ),
  21. CPU_AVX2 = ( << ),
  22. CPU_AES = ( << ),
  23. CPU_ERMS = ( << ), // enhanced 'rep movsb/stosb' instructions
  24. CPU_CLMUL = ( << ) // carryless multiply for CRC
  25. } cpuFeatureFlags;

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

  1. _cpuFeatures = feature_flags();
  1. static uint32_t feature_flags() {
  2. uint32_t result = ;
  3. if (_cpuid_info.std_cpuid1_edx.bits.cmpxchg8 != )
  4. result |= CPU_CX8;
  5. if (_cpuid_info.std_cpuid1_edx.bits.cmov != )
  6. result |= CPU_CMOV;
  7. if (_cpuid_info.std_cpuid1_edx.bits.fxsr != || (is_amd() &&
  8. _cpuid_info.ext_cpuid1_edx.bits.fxsr != ))
  9. result |= CPU_FXSR;
  10. // HT flag is set for multi-core processors also.
  11. if (threads_per_core() > )
  12. result |= CPU_HT;
  13. if (_cpuid_info.std_cpuid1_edx.bits.mmx != || (is_amd() &&
  14. _cpuid_info.ext_cpuid1_edx.bits.mmx != ))
  15. result |= CPU_MMX;
  16. if (_cpuid_info.std_cpuid1_edx.bits.sse != )
  17. result |= CPU_SSE;
  18. if (_cpuid_info.std_cpuid1_edx.bits.sse2 != )
  19. result |= CPU_SSE2;
  20. if (_cpuid_info.std_cpuid1_ecx.bits.sse3 != )
  21. result |= CPU_SSE3;
  22. if (_cpuid_info.std_cpuid1_ecx.bits.ssse3 != )
  23. result |= CPU_SSSE3;
  24. if (_cpuid_info.std_cpuid1_ecx.bits.sse4_1 != )
  25. result |= CPU_SSE4_1;
  26. if (_cpuid_info.std_cpuid1_ecx.bits.sse4_2 != )
  27. result |= CPU_SSE4_2;
  28. if (_cpuid_info.std_cpuid1_ecx.bits.popcnt != )
  29. result |= CPU_POPCNT;
  30. if (_cpuid_info.std_cpuid1_ecx.bits.avx != &&
  31. _cpuid_info.std_cpuid1_ecx.bits.osxsave != &&
  32. _cpuid_info.xem_xcr0_eax.bits.sse != &&
  33. _cpuid_info.xem_xcr0_eax.bits.ymm != ) {
  34. result |= CPU_AVX;
  35. if (_cpuid_info.sef_cpuid7_ebx.bits.avx2 != )
  36. result |= CPU_AVX2;
  37. }
  38. if (_cpuid_info.std_cpuid1_edx.bits.tsc != )
  39. result |= CPU_TSC;
  40. if (_cpuid_info.ext_cpuid7_edx.bits.tsc_invariance != )
  41. result |= CPU_TSCINV;
  42. if (_cpuid_info.std_cpuid1_ecx.bits.aes != )
  43. result |= CPU_AES;
  44. if (_cpuid_info.sef_cpuid7_ebx.bits.erms != )
  45. result |= CPU_ERMS;
  46. if (_cpuid_info.std_cpuid1_ecx.bits.clmul != )
  47. result |= CPU_CLMUL;
  48.  
  49. // AMD features.
  50. if (is_amd()) {
  51. if ((_cpuid_info.ext_cpuid1_edx.bits.tdnow != ) ||
  52. (_cpuid_info.ext_cpuid1_ecx.bits.prefetchw != ))
  53. result |= CPU_3DNOW_PREFETCH;
  54. if (_cpuid_info.ext_cpuid1_ecx.bits.lzcnt != )
  55. result |= CPU_LZCNT;
  56. if (_cpuid_info.ext_cpuid1_ecx.bits.sse4a != )
  57. result |= CPU_SSE4A;
  58. }
  59.  
  60. return result;
  61. }
  1. 至此,基本可以断定这里的判断,是从CPUID中获取的信息,来看CPU是否支持CMPXCHG8指令。
  2.  
  3. 再回过头来看这句:
  1. return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;

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

  1. 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

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

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

  1. inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) {
  2. int mp = os::is_MP();
  3. jint ex_lo = (jint)exchange_value;
  4. jint ex_hi = *( ((jint*)&exchange_value) + );
  5. jint cmp_lo = (jint)compare_value;
  6. jint cmp_hi = *( ((jint*)&compare_value) + );
  7. __asm {
  8. push ebx
  9. push edi
  10. mov eax, cmp_lo
  11. mov edx, cmp_hi
  12. mov edi, dest
  13. mov ebx, ex_lo
  14. mov ecx, ex_hi
  15. LOCK_IF_MP(mp)
  16. cmpxchg8b qword ptr [edi]
  17. pop edi
  18. pop ebx
  19. }
  20. }

可以看出,当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. 8.22 NOIP模拟测试29(B) 爬山+学数数+七十和十七

    T1 爬山 二分最高高度,$O(1)$判断是否可行. #include<iostream> #include<cstdio> #define ll long long usin ...

  2. [LeetCode] 592. Fraction Addition and Subtraction 分数加减法

    Given a string representing an expression of fraction addition and subtraction, you need to return t ...

  3. [LeetCode] 51. N-Queens N皇后问题

    The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens ...

  4. oracle--sqlplus格式化输出

    01,日期格式化输出 SQL> alter session set NLS_DATE_FORMAT='YYYY-MM-DD HH24:mi:ss'; SQL> select sysdate ...

  5. asp.net mvc移除X-AspNet-Version、X-AspNetMvc-Version、Server

    asp.net mvc程序部署到IIS,,返回的HTTP头中包含Server, X-Powered-By, 和 X-AspNet-Version.X-AspNet-Version信息. 这些信息有时给 ...

  6. Nodejs操作MySQL数据库

    https://github.com/mysqljs/mysql   如何用nodejs操作MySql数据呢,其实写法还是简单的, 1.开始在你的node项目中 npm install mysql - ...

  7. 大话设计模式Python实现-策略模式

    策略模式(Strategy Pattern):它定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户. 下面是一个商场活动的实现 #!/usr/bin/e ...

  8. Feign切换client到okhttp无法生效天坑!(附带发生的原因)

    提示:如果只看如何解决问题,请看文章的末尾如何解决这个问题 1. 场景描述 最近项目中使用了feign当做http请求工具来使用.相对于httpclient.resttemplate来说,fegin用 ...

  9. 一个 Github 上使用 HttpClient 的 Sample

    地址:https://github.com/MikeWasson/HttpClientSample 截图: 直接贴代码了: 服务端: [RoutePrefix("api/products&q ...

  10. service的yaml说明

    apiVersion: v1 kind: Service metadata: name: nginx-service labels: app: nginx spec: ports: - port: 8 ...