在代码中获取 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 学习和源码阅读的更多相关文章

  1. Dapper源码学习和源码修改

    之前ORM比较火热,自己也搞了个WangSql,但是感觉比较low,大家都说Dapper性能好,所以现在学习学习Dapper,下面简单从宏观层面讲讲我学习的Dapper. 再了解一个东西前,先得学会使 ...

  2. Dapper源码学习和源码修改(下篇)

    目录: Dapper源码学习和源码修改(上篇主要讲解入参解析) Dapper源码学习和源码修改(下篇主要讲解出参解析) 继上篇讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析 ...

  3. Spring的学习和源码的学习

    PS:Spring中有各种的Templeate,比如jdncTemplate,主要是为了避免各种模板的代码,抽象出来的 PS: @Configration.@Bean是用来替代xml那种解析方式 PS ...

  4. 以CapsNet为例谈深度学习源码阅读

    本文的参考的github工程链接:https://github.com/laubonghaudoi/CapsNet_guide_PyTorch 之前是看过一些深度学习的代码,但是没有养成良好的阅读规范 ...

  5. SpringMVC源码阅读:过滤器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  6. SpringMVC源码阅读:属性编辑器、数据绑定

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  7. SpringMVC源码阅读:拦截器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  8. SpringMVC源码阅读:核心分发器DispatcherServlet

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...

  9. SpringMVC源码阅读:定位Controller

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...

随机推荐

  1. Linux shell中处理

        awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息 awk处理过程: 依次对每一行进行处理,然后输出 awk命令形式: awk ...

  2. Java中设置方法执行的超时时间java.util.concurrent.Future

    java.util.concurrent.Future Future代表一个异步计算的结果.它提供了方法来检查是否计算已经完成,还是正在计算而处于等待状态,并且也提供了获取计算结果 方法.当计算完成后 ...

  3. 使用windows脚本移动文件

    1. 移动脚本 在部署web项目时,一般需要将打包的war包发布到Tomcat目录下,所以自己就在网上查找资料写了一个简略的移动文件的脚本,如下: @echo off echo "使用bat ...

  4. SQL 查询存储过程

    select distinct name from syscomments a,sysobjects b where a.id=b.id and b.xtype='p' --and text like ...

  5. 极客无极限 一行HTML5代码引发的创意大爆炸

    摘要:一行HTML5代码能做什么?国外开发者Jose Jesus Perez Aguinaga写了一行HTML5代码的文本编辑器.这件事在分享到Code Wall.Hacker News之后,引起了众 ...

  6. Bootstrap 分页、标签、徽章、超大屏幕、页面标题

    分页(pagination), 是一种无序列表 1.默认的分页(.pagination) 代码示例: <ul class="pagination"> <li> ...

  7. loj#2054. 「TJOI / HEOI2016」树

    题目链接 loj#2054. 「TJOI / HEOI2016」树 题解 每次标记覆盖整棵字数,子树维护对于标记深度取max dfs序+线段树维护一下 代码 #include<cstdio> ...

  8. [CF490F]Treeland Tour(线段树合并)

    树上LIS:树上找一条简单路径的子序列使点权严格单增,最大化长度. 原题数据过小,用线段树合并可以做到$O(n\log n)$. 每个点用一棵线段树维护以每个权值为结尾的LIS最长长度,线段树合并时更 ...

  9. BZOJ.3585.mex(线段树)

    题目链接 题意:多次求区间\(mex\). 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么 ...

  10. BZOJ4714 : 旋转排列

    对于每个$k$,问题等价于求有多少置换满足: 1.存在一个循环长度为$k$ 2.任意一个循环长度$\geq 2$ 枚举这种环的个数$t$: 设$g_t$表示至少有$kt$个人分成$t$个长度为$k$的 ...