昨天剛看完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而後又遲遲沒有東西,那就一堆內存在等了。