以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. [LeetCode] 272. Closest Binary Search Tree Value II 最近的二分搜索树的值之二

    Given a non-empty binary search tree and a target value, find k values in the BST that are closest t ...

  2. java web开发入门八(ssm整合)基于intellig idea

    ssm整合 一.导入相关包 二.开发流程 1.写entity package com.eggtwo.euq.entity; import java.io.Serializable; import ja ...

  3. 第19课 lambda vs std::bind

    一. std::bind (一)std::bind实现的关键技术 [编程实验]探索bind原理,实现自己的bind函数 #include <iostream> #include <t ...

  4. windows php 下的各个版本的资源下载地址

    历史版本: https://windows.php.net/downloads/releases/archives/ 新版本: https://windows.php.net/download/ 找的 ...

  5. ASPNETCore开源日志面板 :LogDashboard

    LogDashboard logdashboard是在github上开源的aspnetcore项目, 它旨在帮助开发人员排查项目运行中出现错误时快速查看日志排查问题 通常我们会在项目中使用nlog.l ...

  6. BCompare注册文件+密钥被撤销解决方案

    注册码: rssAPVg2OpBjDVo3E0DhGWrjPIq0hsTSuNz13wTuzVHfb2mRgO9bZKn9Bl42D5YEyMSYPXsxzcb08dqbRlbzWNJzJXE6YVa ...

  7. 使用numpy处理数组

    def fun_ndarray(): a = [[1,2,7], [-6,-2,-3], [-4,-8,-55] ] b = np.array(a) b = np.abs(b)#取数组的绝对值 pri ...

  8. Java反射获取泛型类型

    本文链接:https://blog.csdn.net/hongxingxiaonan/article/details/49202613 在Java反射技术的应用中,取得一个类的成员.方法和构造函数相对 ...

  9. MVC下通过jquery的ajax调用webapi

    如题 jquery的应用,不会的自己去补. 创建一个mvc项目,新建控制器.视图如下: 其中data控制器负责向前台提供数据,home控制器是一个简单的访问页控制器. data控制器代码如下: pub ...

  10. Golang程序调试工具介绍(gdb vs dlv)

    原文:http://lday.me/2017/02/27/0005_gdb-vs-dlv/ 通过log库输出日志,我们可以对程序进行异常分析和问题追踪.但有时候,我也希望能有更直接的程序跟踪及定位工具 ...