JAVA 1.7并发之LinkedTransferQueue原理理解

昨天刚看完BlockingQueue以为好高级啊,今天扫到1.7就发现了升级版。。。。java

若是对内容以为不够充分,能够去看http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf node

就是做者的论文啦,纯英文。。。比较难啃,可是我觉得逻辑上比看代码容易理解,其实代码什么u啊h啊看得很混数据结构

 

LinkedTransferQueue并发

 

起源: 我以为是这样的,以前的BlockingQueue是对 读取 或者 写入 锁定整个队列,因此在比较繁忙的时候,各类锁比较耗时app

而当时有一个SynchronizedQueue其实不能叫Queue,由于只能放一个物件,要么有一个物件在等人拿,要么有一个空等人放less

根据这个原理,诞生了LinkedTransferQueue,利用CompareAndSwap进行一个无阻塞的队列,针对每个操做进行处理样你们就不用抢得那么辛苦了spa

 

数据结构线程

在类的内部保持着一个栈,基本单位是node,根据 hasData区分里面有两种元素,要么是 Data 要么是 Reservation,不会同时存在rest

而且有一个变量head指向最前面的node,没东西则是nullblog

Node

{

  isData    是否是数据,是的话item放具体东西

  item   若是不是数据则为null

  next   下一个节点

  waiter  若是不是数据则是reservation,有一个线程在等待

}

 

过程:

整个存取过程分红两部分

 1:MATCH(原节点,新节点)

for (;;) {                            // restart on append race

            for (Node h = head, p = h; p != null;) { // 若是头结点为空则跳过,非空进去找第一个可用节点
                boolean isData = p.isData;
                Object item = p.item;
                if (item != p && (item != null) == isData) { // 判断原节点可用性,如data的item应该是数值,若是是null则代表用过了
                    if (isData == haveData)   // 两个节点是相同类型,不用match了,去下一步
                        break;
                    if (p.casItem(item, e)) { // 节点不一样类型,match成功,更改原节点item,代表不可用
                        for (Node q = p; q != h;) {//什么,我竟然不是head节点了?我要让它指向我!
                            Node n = q.next;  // update by 2 unless singleton
                            if (head == h && casHead(h, n == null ? q : n)) {
                                h.forgetNext();
                                break;
                            }                 // advance and retry
                            if ((h = head)   == null ||
                                (q = h.next) == null || !q.isMatched())
                                break;        // unless slack < 2
                        }
                        LockSupport.unpark(p.waiter);//根据原节点的类型,reservation则叫人收货,data则叫null收货
                        return LinkedTransferQueue.<E>cast(item);//根据原节点的类型,reservation则返回null,data则返回数据
                    }
                }
                Node n = p.next;//下一个节点
                p = (p != n) ? n : (h = head); // Use head if p offlist
            }

  重点是找出第一个可用节点,若是是null则跳过,若是与进来的节点相同(原本就有data,还放data)也跳过,若是不一样(原本是data,如今是reservation,返回data值 / 原本是reservation,如今是data,叫人来收货,返回reservation值=空)

 2:处理节点

if (how != NOW) {                 // No matches available
                if (s == null)
                    s = new Node(e, haveData);
                Node pred = tryAppend(s, haveData);//尝试添加新node
                if (pred == null)
                    continue retry;           // 不成功则重试整个过程
                if (how != ASYNC)
                    return awaitMatch(s, pred, e, (how == TIMED), nanos);//根据参数,等不等别人放数据,拿数据,等多久
            }
            return e; // not waiting

  MATCH失败了才会进入这个环节,把新节点放进栈内,并根据参数决定马上返回或者等待返回

EXAMPLES

1:Head->Data    Input->Data

Match:      根据他们的属性 发现 cannot match ,由于是同类的

处理节点:   因此把新的data放在原来的data后面,而后head日后移一位,Reservation同理

HEAD=DATA->DATA

 

2:Head->Data    Input->Reservation  (取数据)

Match:      成功match,就把Data的item变为reservation的值(null,有主了),而且返回数据。

处理节点:  没动,head还在原地

HEAD=DATA(用过)

 

3:Head->Reservation  Input->Data(放数据)

Match:       成功match,就把Reservation的item变为Data的值(有主了),而且叫waiter来取

处理节点:  没动

HEAD=RESERVATION(用过)

 

总结:LinkedTransferQueue经过CAS尝试放入data或增长reservation。

消耗小于把整个队列锁掉,可是在并发特别高的状况下你们抢着尝试同样会影响速度

至于为何跨过了1.6到1.7这个类才出现我以为有点神奇

 

简单用法介绍------------------------------------------------------------------------------------------------------

存:

put();   放元素进去队列,注意队列是能够无限长的

add();   同上

transfer();  这个是重点,若是队列中有人发现有人在等,则直接给那我的(有一个参数waiter指定了在等的线程)

若是没人在等,就放进队列

取:

poll();  当即返回,若是没有元素就是空

take(); 若是没有元素,那就等

PS:最好是用poll而后本身处理空的情况,若是全是take而后又迟迟没有东西,那就一堆内存在等了。

相关文章
相关标签/搜索