Unsafe 与 LockSupport

1.Unsafe

java concurrent 包的基础是CAS, 而进行CAS操作的就是这个 Unsafe类.

废话不说了, 直接上代码:

Unsafe 类是Java中的保护类, 在外面包中使用通常通过反射获取 Unsafe类实例

/**
 * 
 * Unsafe 类是java中的保护类, 所以就通过这种方式获取(ps 也可以在命令行中指定所加载的包是受保护的)
 * Created by xjk on 2016/5/13.
 */
public class UnSafeClass {

    private static Unsafe unsafe;

    static{
        try {
            // 通过反射的方式获取unsafe类
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe)field.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static Unsafe getInstance(){
        return unsafe;
    }
}

再来一段Unsafe类在 concurrent 包中的使用

import com.lami.tuomatuo.search.base.concurrent.unsafe.UnSafeClass;
import sun.misc.Unsafe;

/**
 * Created by xjk on 1/13/17.
 */
public class Node<E> {
    volatile E item;
    volatile Node<E> next;

    Node(E item){
        /**
         * Stores a reference value into a given Java variable.
         * <p>
         * Unless the reference <code>x</code> being stored is either null
         * or matches the field type, the results are undefined.
         * If the reference <code>o</code> is non-null, car marks or
         * other store barriers for that object (if the VM requires them)
         * are updated.
         * @see #putInt(Object, int, int)
         *
         * 将 Node 对象的指定 itemOffset 偏移量设置 一个引用值
         */
        unsafe.putObject(this, itemOffset, item);
    }

    boolean casItem(E cmp, E val){
        /**
         * Atomically update Java variable to <tt>x</tt> if it is currently
         * holding <tt>expected</tt>.
         * @return <tt>true</tt> if successful
         * 原子性的更新 item 值
         */
        return unsafe.compareAndSwapObject(this, itemOffset, cmp, val);
    }

    void lazySetNext(Node<E> val){
        /**
         * Version of {@link #putObjectVolatile(Object, long, Object)}
         * that does not guarantee immediate visibility of the store to
         * other threads. This method is generally only useful if the
         * underlying field is a Java volatile (or if an array cell, one
         * that is otherwise only accessed using volatile accesses).
         *
         * 调用这个方法和putObject差不多, 只是这个方法设置后对应的值的可见性不一定得到保证,
         * 这个方法能起这个作用, 通常是作用在 volatile field上, 也就是说, 下面中的参数 val 是被volatile修饰
         */
        unsafe.putOrderedObject(this, nextOffset, val);
    }

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     *
     * 原子性的更新 nextOffset 上的值
     *
     */
    boolean casNext(Node<E> cmp, Node<E> val){
        return unsafe.compareAndSwapObject(this, nextOffset, cmp, val);
    }

    private static Unsafe unsafe;
    private static long itemOffset;
    private static long nextOffset;

    static {
        try {
            unsafe = UnSafeClass.getInstance();
            Class<?> k = Node.class;
            itemOffset = unsafe.objectFieldOffset(k.getDeclaredField("item"));
            nextOffset = unsafe.objectFieldOffset(k.getDeclaredField("next"));
        }catch (Exception e){

        }
    }
}

上面的代码是一个链表节点的代码, 主要方法:

1. unsafe.putObject(this, itemOffset, item)
   直接在对象的itemOffset位置设置item的引用

2. unsafe.compareAndSwapObject(this, itemOffset, cmp, val)
   通过比较itemOffset上是否为引用cmp的值, 来设置val

3. unsafe.putOrderedObject(this, nextOffset, val)
   在对象的itemOffset位置设置item的引用, 只不过这个方法的可见性比直接用 putObject 方法低一点, 其他的线程要过一段时间才可见

这三个方法中 putObject, compareAndSwapObject 是常用方法, putOrderedObject相对来说就神秘一下, 就下来我们就聊这个方法

putOrderedObject: 将这个方法名拆成 put ordered Object, 这下就好理解一点, 按照order来进行写; 在底层操作时使用 store-store barrier(), 不会被Java的JIT重新排序; 它通常用于元素 nulling out fileds (即删除), 在AtomicXX 中通常用于 LazySet;

As probably the last little JSR166 follow-up for Mustang, we added a "lazySet" method to the Atomic classes (AtomicInteger, AtomicReference, etc). This is a niche method that is sometimes useful when fine-tuning code using non-blocking data structures. The semantics are that the write is guaranteed not to be re-ordered with any previous write, but may be reordered with subsequent operations (or equivalently, might not be visible to other threads) until some other volatile write or synchronizing action occurs).

The main use case is for nulling out fields of nodes in non-blocking data structures solely for the sake of avoiding long-term garbage retention; it applies when it is harmless if other threads see non-null values for a while, but you'd like to ensure that structures are eventually GCable. In such cases, you can get better performance by avoiding the costs of the null volatile-write. There are a few other use cases along these lines for non-reference-based atomics as well, so the method is supported across all of the AtomicX classes.

For people who like to think of these operations in terms of machine-level barriers on common multiprocessors, lazySet provides a preceeding store-store barrier (which is either a no-op or very cheap on current platforms), but no store-load barrier (which is usually the expensive part of a volatile-write).

这三句话是 Doug Lea 写的 链接, 调用这个方法产生的效果是: write操作不会和前面的写操作重排序, 但是可能会被随后的操作重排序(即随后的操作中可能不可见), 直到其他的volatile写或同步事件发生;
主要用于无阻塞数据结构的 nulling out, 就像上面的 lazySet 方法, 它是在 ConcurrentLinkedQueue中删除节点中使用的, 比如下面的删除原队列头节点:


/**
 * Tries to CAS head to p, If successfully, repoint old head to itself
 * as sentinel for succ(), blew
 *
 * 将节点 p设置为新的节点(这是原子操作),
 * 之后将原节点的next指向自己, 直接变成一个哨兵节点(为queue节点删除及garbage做准备)
 *
 * @param h
 * @param p
 */
final void updateHead(Node<E> h, Node<E> p){
    if(h != p && casHead(h, p)){
        h.lazySetNext(h);
    }
}

删除节点后 调用 lazySetNext(其实是 putOrderedObject) 来将自己的next指向自己
ps: 这里为啥不调用 putObject呢, 原因非常简单, putOrderedObject 使用 store-store barrier屏障, 而 putObject还会使用 store-load barrier 屏障(对于Java中的指令屏障不了解的直接可以参考 Java并发编程艺术)

2. unsafe总结:

unsafe类是沟通java代码和计算机底层的一把神奇的钥匙 , 而在实际的通常用于原子性的改变对象中的某个变量

3. LockSupport

LockSupport 的主要功能是提供线程一个"许可", 通过这个"许可"来控制线程的阻塞和恢复; 它主要有下面这几个方法

/** 
 * 阻塞当前线程, 直到调用 LockSupport.unpark(Thread), 或进行线程中断
 * 若线程是通过中断的方式进行唤醒, 则 不会抛出异常, 且中断标示不会更改, 需要调用
 * Thread.interrupted() 才能清除
 */
1. LockSupport.park();

/** 和上面的差不多, 只是加个object(在jstack时,有了这个对象方便定位问题)
* ps: 不是对这个对象阻塞, 只是当前线程阻塞, 这个object只是定位问题的参数
*/
2. LockSupport.park(Object object);

/** 和上面的差不多, 只是加个timeout
*/
3. LockSupport.park(lont timeout);

/** 和上面的差不多, 只是加个deadline(绝对时间, 就是未来某刻的时间戳)
*/
4. LockSupport.parkUntil(lont deadline);

/** 唤醒线程
*/
5. LockSupport.unpark(Thread);

LockSupport和Unsafe都是 java并发编程的基础类, 它们的api都比简单, 写几个小demo就可以了

参考资料:
lazySet
bugs.java
unsafe.cpp

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容