在代码中获取 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. 在Spring中配置SQL server 2000

    前言 Lz主要目的是在Spring中配置SQL server 2000数据库,但实现目的的过程中参差着许多SQL server 2000的知识,也包罗在本文记载下来!(Lz为什么要去搞sql serv ...

  2. 虚拟机Ping不通主机解决

    最近,装了一个虚拟机(Ubuntu-server-12.04),使用的桥接的方式.装完之后发现,主机可以ping通虚拟机,但是虚拟机可以ping通除主机之外的所有的IP(包括网关,DNS,还有其他的i ...

  3. SPOJTLE - Time Limit Exceeded(高维前缀和)

    题意 题目链接 题目的意思是给一个数组C,长度为n,每个数字的范围是2^m,然后要求构造一个数组a,满足 1.a[i] % C[i] !=0 ; 2.a[i] < 2^m ; 3.a[i] &a ...

  4. 潭州课堂25班:Ph201805201 并发(通信) 第十三课 (课堂笔记)

    from multiprocessing import Process # 有个 url 列表 ,有5个 url ,一次请求是1秒,5个5秒 # 要求1秒把 url 请求完, a = [] # 在进程 ...

  5. Java基础-多线程-②多线程安全问题

    什么是线程的安全问题? 上一篇 Java基础-多线程-①线程的创建和启动 我们说使用实现Runnable接口的方式来创建线程,可以实现多个线程共享资源: class Dog implements Ru ...

  6. 回顾MySQL中的事务特征

    一.事务定义Transaction事务:一个最小的不可再分的工作单元:通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)一个完整的业务需要批量的DML(insert. ...

  7. Swift中String与NSDate的互相转换

    其实每种编程语言,我都觉得String和日期对象的相互转换是一种十分麻烦的事情,Swift也不例外.这篇博客记录了我学到的String与NSDate的互相转换方法,供大家参考. 从String转为NS ...

  8. UIView的层次结构–code

    转:http://blog.dongliwei.cn/archives/uiview-tree-code // Recursively travel down the view tree, incre ...

  9. 移动基于Percona XTRADB Cluster的大数据解决方式

    移动基于Percona XTRADB Cluster的大数据解决方式          一.移动的去IOE之旅      近期由于"棱镜门"事件的曝光.引起了国家对信息安全问题的注 ...

  10. RPC简述

    在某种意义上,WebService.REST均是RPC的实现,那么RPC的发展过程如何呢?本文参考了wikipedia,对RPC做一下简要摘记. RPC(RemoteProcedureCall),是进 ...