JDK 1.8 sun.misc.Unsafe类CAS底层实现
在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足。
在sun.misc.Unsafe中CAS方法如下:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
在JDK1.8中只有上述三个CAS方法,其方法参数含义为:var1为待修改的field对象;var2为field对象偏移量,为long型;var4为期望值;var5或var6为替换值,当var1[offset] == var4则设置var1[offset] = var5(var6)。
这三个方法都是native方法,可以查看hotspot源码查看其底层实现:(hotspot/src/share/vm/prims/unsafe.cpp)
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
{CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)},
{CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)},
{CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z", FN_PTR(Unsafe_CompareAndSwapLong)},
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
UnsafeWrapper("Unsafe_CompareAndSwapObject");
oop x = JNIHandles::resolve(x_h); // 更新值
oop e = JNIHandles::resolve(e_h); // 期望值
oop p = JNIHandles::resolve(obj); // 更新对象
HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset); // 根据偏移量offset获取内存中的具体位置
oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e, true); // 调用方法执行CAS操作
jboolean success = (res == e); // 如果返回值res==e则表明满足compare条件,swap成功
if (success)
update_barrier_set((void*)addr, x); // 更新memory barrier
return success;
UNSAFE_END 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 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
先来看下Unsafe_CompareAndSwapObject方法,该方法通过调用index_oop_from_field_offset_long方法找到需要执行CAS对象的具体地址,然后调用atomic_compare_exchange_oop方法执行CAS操作。
继续深入atomic_compare_exchange_oop方法看一下,源码如下
// 声明在hotspot/src/share/vm/oops/oop.hpp
static oop atomic_compare_exchange_oop(oop exchange_value,
volatile HeapWord *dest,
oop compare_value,
bool prebarrier = false); // 定义在hotspot/src/share/vm/oops/oop.inline.hpp
inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value,
volatile HeapWord *dest,
oop compare_value,
bool prebarrier) {
if (UseCompressedOops) {
if (prebarrier) {
update_barrier_set_pre((narrowOop*)dest, exchange_value);
}
// encode exchange and compare value from oop to T
narrowOop val = encode_heap_oop(exchange_value);
narrowOop cmp = encode_heap_oop(compare_value); narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp);
// decode old from T to oop
return decode_heap_oop(old);
} else {
if (prebarrier) {
update_barrier_set_pre((oop*)dest, exchange_value);
}
return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value);
}
}
在atomic_compare_exchange_oop方法中,核心的CAS操作最终是调用了Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数或者Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数。
Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数虽然有很多重载函数,但最终都是调用的下面的函数:
// hotspot/src/share/vm/runtime/Atomic.cpp
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
assert(sizeof(jbyte) == , "assumption.");
uintptr_t dest_addr = (uintptr_t)dest;
uintptr_t offset = dest_addr % sizeof(jint);
volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
jint cur = *dest_int; // 对象当前值
jbyte* cur_as_bytes = (jbyte*)(&cur); // 当前值cur的地址
jint new_val = cur;
jbyte* new_val_as_bytes = (jbyte*)(&new_val); // new_val地址
// new_val存exchange_value,后面修改则直接从new_val中取值
new_val_as_bytes[offset] = exchange_value;
// 比较当前值与期望值,如果相同则更新,不同则直接返回
while (cur_as_bytes[offset] == compare_value) {
// 调用汇编指令cmpxchg执行CAS操作,期望值为cur,更新值为new_val
jint res = cmpxchg(new_val, dest_int, cur);
if (res == cur) break;
cur = res;
new_val = cur;
new_val_as_bytes[offset] = exchange_value;
}
// 返回当前值
return cur_as_bytes[offset];
}
Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数在不同系统中都有各自的声明,但是最终都是调用的下面的函数:
// hotspot/src/os_cpu/solaris_x86/vm/Atomic_solaris_x86.inline.hpp // This is the interface to the atomic instruction in solaris_i486.s.
jlong _Atomic_cmpxchg_long_gcc(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp); inline jlong _Atomic_cmpxchg_long(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp) {
#ifdef AMD64
__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;
#else
return _Atomic_cmpxchg_long_gcc(exchange_value, dest, compare_value, os::is_MP()); #if 0
// The code below does not work presumably because of the bug in gcc
// The error message says:
// can't find a register in class BREG while reloading asm
// However I want to save this code and later replace _Atomic_cmpxchg_long_gcc
// with such inline asm code: volatile jlong_accessor evl, cvl, rv;
evl.long_value = exchange_value;
cvl.long_value = compare_value;
int mp = os::is_MP(); __asm__ volatile ("cmp $0, %%esi\n\t"
"je 1f \n\t"
"lock\n\t"
"1: cmpxchg8b (%%edi)\n\t"
: "=a"(cvl.words[]), "=d"(cvl.words[])
: "a"(cvl.words[]), "d"(cvl.words[]),
"b"(evl.words[]), "c"(evl.words[]),
"D"(dest), "S"(mp)
: "cc", "memory");
return cvl.long_value;
#endif // if 0
#endif // AMD64
}
在这个方法中废弃了32位系统的cmpxchg8b指令实现CAS操作方式,只提供了AMD64位操作系统cmpxchgq指令实现方式。
从上面可以看出无论是Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数或者Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数,二者最终都是通过一条汇编指令实现CAS操作的。
Unsafe_CompareAndSwapInt和Unsafe_CompareAndSwapLong两个方法都是调用Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数实现的,这个函数上面已经解释过。
综合上面的源码分析,可以知道sun.misc.Unsafe类中的CAS都是通过一条汇编指令实现的,这也就不难理解为什么这个操作可以保证原子性了。
参考文章:
http://blog.csdn.net/qqqqq1993qqqqq/article/details/75211993
https://www.cnblogs.com/dennyzhangdd/p/6734933.html
JDK 1.8 sun.misc.Unsafe类CAS底层实现的更多相关文章
- Java的sun.misc.Unsafe类
阅读目录 前言 Unsafe类的作用 获取Unsafe对象 Unsafe类中的API 前言 以下sun.misc.Unsafe源码和demo基于jdk1.7: 最近在看J.U.C里的源码,很多都用到了 ...
- Java sun.misc.unsafe类
Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的.如果你想搞破坏,可以使用Unsafe这个类.这个类是属于sun.*API中的类,并且它不是J2SE中 ...
- Java sun.misc.Unsafe类的学习笔记
Java未开源的Unsafe类 Unsafe类可以为我们提供高效并且线程安全方式操作变量,直接和内存数据打交道. 获取Unsafe实体的方法 private static Unsafe getUnsa ...
- 并发编程之sun.misc.Unsafe类
1.Unsafe知识点整理 2.代码: package com.javabasic.unsafe; import java.lang.reflect.Field; import sun.misc.Un ...
- sun.misc.unsafe类的使用
http://blog.csdn.net/fenglibing/article/details/17138079
- eclipse无法访问sun.misc.Unsafe类的解决办法
参考:https://www.cnblogs.com/duanxz/p/6090442.html
- java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值
在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...
- sun.misc.Unsafe的理解
以下sun.misc.Unsafe源码和demo基于jdk1.7: 最近在看J.U.C里的源码,很多都用到了sun.misc.Unsafe这个类,一知半解,看起来总感觉有点不尽兴,所以打算对Unsaf ...
- sun.misc.Unsafe 详解
原文地址 译者:许巧辉 校对:梁海舰 Java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的.但是,有一种方式可以有意的执行一些不安全.容易犯错的操作,那就是使用Unsa ...
随机推荐
- rcc时钟
1.时钟源 在 STM32 中,一共有 5 个时钟源,分别是 HSI . HSE . LSI . LSE . PLL . ①HSI 是高速内部时钟, RC 振荡器,频率为 8MHz : ②HSE 是高 ...
- Docker系列(三)Dockerfile 离线构建镜像
一.Dockfile介绍: Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfil ...
- [转]Java调用Javascript、Python算法总结
最近项目中经常需要将Javascript或者Python中的算法发布为服务,而发布Tomcat服务则需要在Java中调用这些算法,因此就不免要进行跨语言调用,即在Java程序中调用这些算法. 不管是调 ...
- angularjs 学习小结
1.过滤器的使用 <!DOCTYPE html> <html> <head> <meta charset="{CHARSET}"> ...
- voc-fcn-alexnet网络结构理解
一.写在前面 fcn是首次使用cnn来实现语义分割的,论文地址:fully convolutional networks for semantic segmentation 实现代码地址:https: ...
- C# .NET newtonsoft.json 多版本冲突解决
A.DLL 引用了6.0 的 newtonsoft.json (V2 运行时),B.DLL 引用了10.0 的 newtonsoft.json (V4 运行时). 可以在.CONFIG RUNTIM ...
- Vue+Typescript项目中使用echarts
方案一:推荐 在typescript+Vue的项目中引用echarts,为了加强引用,引入echarts和@types/echarts两个包,一个是工程依赖,一个是声明依赖. npm install ...
- python基础知识14---迭代器、生成器、面向过程编程
阅读目录 一 迭代器 二 生成器 三 面向过程编程 一 迭代器 1 迭代的概念 #迭代器即迭代的工具,那什么是迭代呢? #迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初 ...
- AngularJS 1.x 思维索引
我们在这里不讨论Angular2和Angular4,因为其完全重写,其实已经不叫AngularJS了. AngularJS的缺陷: 性能问题:通过检查脏值进行数据更新,当数据不断增加时,检查的效率就不 ...
- myeclipse2018安装教程(附下载地址)
这两天在网上找myeclipse2018的安装包和破解工具,很分散,并且破解失败很多,现在总结下 myeclipse2018下载地址: https://pan.baidu.com/s/1NV7cAsN ...