Unsafe 学习和源码阅读
在代码中获取 Unsafe 对象的方法:
// 在 AtomicInteger 里面是这么用的private static final Unsafe unsafe = Unsafe.getUnsafe();
// 获取内存的偏移地址,有点奇怪啊,为啥是获取这个类的,而不是这个对象的呢?static { try { valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); }}
1.8 之前 Unsafe 的包路径为:
package sun.misc;
而到了 Java 9,它的包路径改成了下面这个,说明这个类已经开放使用。
package jdk.internal.misc;
获取 Unsafe 对象,这个是 openjdk 里的方法,通过反射获得。
static { Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");}
1)内存管理
可以分配内存了哦
public native long allocateMemory(long var1);
public native long reallocateMemory(long var1, long var3);
public native void setMemory(Object var1, long var2, long var4, byte var6);
2)CAS 乐观锁
如果变量没有被其他线程改变的话,会一直尝试去修改变量,直到变成修改值。
CAS,Compare and Swap 即比较并交换,设计并发算法时常用到的一种技术,java.util.concurrent 包全完建立在 CAS 之上,没有 CAS 也就没有此包,可见 CAS 的重要性。
当前的处理器基本都支持 CAS,只不过不同的厂家的实现不一样罢了。CAS 有三个操作数:内存值 V、旧的预期值 A、要修改的值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值修改为 B 并返回 true,否则什么都不做并返回 false。
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);
这里的参数是,对象,偏移量,期望值,修改值。
每次都是先去获取当前的值,然后再进行修改,所以如果有机会的话是一定会成功的。
获取偏移量的方法:
valueOffset = unsafe.objectFieldOffset(AtomicBoolean.class.getDeclaredField("value"));
还有:
public native long staticFieldOffset(Field var1);
public native long objectFieldOffset(Field var1);
public native Object staticFieldBase(Field var1);
3)supportlock
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
park类似于wait,unpark类似于notify。
4)cas 源码分析
具体见 unsafe.cpp (版本:openjdk 9 )
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { oop p = JNIHandles::resolve(obj); //获取对象的变量的地址 jint* addr = (jint *)index_oop_from_field_offset_long(p, offset); //调用Atomic操作 return (jint)(Atomic::cmpxchg(x, addr, e)) == e;} UNSAFE_END
进入 atomic.hpp,大意就是先去获取一次结果,如果结果和现在不同,就直接返回,因为有其他人修改了;否则会一直尝试去修改。直到成功。
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) { STATIC_ASSERT(sizeof(jbyte) == 1); volatile jint* dest_int = static_cast<volatile jint*>(align_ptr_down(dest, sizeof(jint))); size_t offset = pointer_delta(dest, dest_int, 1); jint cur = *dest_int; jbyte* cur_as_bytes = reinterpret_cast<jbyte*>(&cur); // current value may not be what we are looking for, so force it // to that value so the initial cmpxchg will fail if it is different cur_as_bytes[offset] = compare_value; // always execute a real cmpxchg so that we get the required memory // barriers even on initial failure do { // value to swap in matches current value ... jint new_value = cur; // ... except for the one jbyte we want to update reinterpret_cast<jbyte*>(&new_value)[offset] = exchange_value; jint res = cmpxchg(new_value, dest_int, cur, order); if (res == cur) break; // success // at least one jbyte in the jint changed value, so update // our view of the current jint cur = res; // if our jbyte is still as cur we loop and try again } while (cur_as_bytes[offset] == compare_value); return cur_as_bytes[offset];}
5)CAS的缺点
CAS 看起来很美,但这种操作显然无法涵盖并发下的所有场景,并且 CAS 从语义上来说也不是完美的,存在这样一个逻辑漏洞:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了 B,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个漏洞称为 CAS 操作的 "ABA" 问题。java.util.concurrent 包为了解决这个问题,提供了一个带有标记的原子引用类 "AtomicStampedReference",它可以通过控制变量值的版本来保证 CAS 的正确性。不过目前来说这个类比较 "鸡肋",大部分情况下 ABA 问题并不会影响程序并发的正确性,如果需要解决 ABA 问题,使用传统的互斥同步可能回避原子类更加高效。
Unsafe 学习和源码阅读的更多相关文章
- Dapper源码学习和源码修改
之前ORM比较火热,自己也搞了个WangSql,但是感觉比较low,大家都说Dapper性能好,所以现在学习学习Dapper,下面简单从宏观层面讲讲我学习的Dapper. 再了解一个东西前,先得学会使 ...
- Dapper源码学习和源码修改(下篇)
目录: Dapper源码学习和源码修改(上篇主要讲解入参解析) Dapper源码学习和源码修改(下篇主要讲解出参解析) 继上篇讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析 ...
- Spring的学习和源码的学习
PS:Spring中有各种的Templeate,比如jdncTemplate,主要是为了避免各种模板的代码,抽象出来的 PS: @Configration.@Bean是用来替代xml那种解析方式 PS ...
- 以CapsNet为例谈深度学习源码阅读
本文的参考的github工程链接:https://github.com/laubonghaudoi/CapsNet_guide_PyTorch 之前是看过一些深度学习的代码,但是没有养成良好的阅读规范 ...
- SpringMVC源码阅读:过滤器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:属性编辑器、数据绑定
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:拦截器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:核心分发器DispatcherServlet
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...
- SpringMVC源码阅读:定位Controller
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...
随机推荐
- PHP7和PHP5在安全上的区别[更新]
0X01 前言 本篇文章大多为转载,但是修正了一些不正确的说法,对某些功能点的变更指出具体是哪个版本变更,加入了一些小更新. (原文地址:https://www.freebuf.com/article ...
- spark加载模型与gRPC与JSF与JDQ冲突
spark与JSF冲突解决方式 <dependency> <groupId>org.apache.spark</groupId> <artifactId> ...
- 将文件打包成apk
Android Studio的打包成apk文件 https://blog.csdn.net/woaichimahua/article/details/54427528
- CentOS 7 下 RabbitMQ 集群搭建
环境 10.0.0.20 node1 10.0.0.21 node2 10.0.0.22 node3 搭建(在所有节点执行) 添加EPEL源 [root@node1 ~]# rpm -Uvh http ...
- Android MediaPlayer架构 -- 前言小知识点(二)
本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教. 在frameworks\av\media\libmedia\mediaplayer.cpp中会有语句:const sp& ...
- 17、python对内存的使用
python对内存的使用 浅拷贝和深拷贝 所谓浅拷贝就是对引用的拷贝(只拷贝父对象) 所谓深拷贝就是对对象的资源的拷贝 解释一个例子: import copy a = [1,2,3,['a','b', ...
- SpringBoot(八):系统错误统一拦截器
在日常 web 开发中发生了异常,往往需要通过一个统一的 异常处理,来保证客户端能够收到友好的提示.本文将会介绍 Spring Boot 中的 全局统一异常处理. Springboot的全局异常查是通 ...
- Excel分组快速自动填充编号
在Excel自动填充很简单,但如果按分组等条件进行填充就有点麻烦了 说麻烦可能是你并没有搞清楚到底如何才能实现你的需求 下图是客户提供的Excel数据,我需要将下面的数据导入到数据库中,因为客户在 ...
- alpha to coverage
alpha to coverage 在游戏中,经常使用带有半透明信息纹理的多边形模型来模拟复杂的物体,例如,草.树叶.铁丝网等.如果使用真正的模型,一颗边缘参差不齐的小草可能就要消耗掉几百个多边形:然 ...
- 魅族便签,是否能成为国内便签应用的No.1?
继前不久锤子科技推出便签 Android 新版后,近期魅族在PRO 6公布会上也公布了最新的魅族便签应用.这一次魅族把便签应用拓展到了整个Android体系,也就是说.其它不论什么的Android手机 ...