3.集--LinkedTransferQueue得知
近期在閱讀開源項(xiàng)目里,發(fā)現(xiàn)有幾個(gè)project都不盡同樣地使用LinkedTransferQueue這個(gè)數(shù)據(jù)結(jié)構(gòu)。比方netty,grizzly,xmemcache,Bonecp。
Bonecp還擴(kuò)展出一個(gè)BoundTransferQueue。
LinkedTransferQueue最早出如今JSR66R(一個(gè)輕量級(jí)并行運(yùn)行框架)包中。眼下已合并到JDK7中。
JSR66的負(fù)責(zé)人正是大名頂頂?shù)腄oug Lea.
盡管LinkedTransferQueue被集成在JDK7中,但眼下主流的JDK平臺(tái)仍然是JDK6。以致開源項(xiàng)目開發(fā)人員都不迫及地把他集成在自已的項(xiàng)目中。
Doug Lea說(shuō)LinkedTransferQueue是一個(gè)聰明的隊(duì)列。他是ConcurrentLinkedQueue,?
SynchronousQueue (in “fair” mode), and unbounded LinkedBlockingQueue的超集。
有一篇論文討論了其算法與性能:地址:http://www.cs.rice.edu/~wns1/papers/2006-PPoPP-SQ.pdf
LinkedTransferQueue實(shí)現(xiàn)了一個(gè)重要的接口TransferQueue,該接口含有以下幾個(gè)重要方法:
1. transfer(E e)
?? 若當(dāng)前存在一個(gè)正在等待獲取的消費(fèi)者線程。即立馬移交之;否則,會(huì)插入當(dāng)前元素e到隊(duì)列尾部,而且等待進(jìn)入堵塞狀態(tài)。到有消費(fèi)者線程取走該元素。
2. tryTransfer(E e)
?? 若當(dāng)前存在一個(gè)正在等待獲取的消費(fèi)者線程(使用take()或者poll()函數(shù))。使用該方法會(huì)即刻轉(zhuǎn)移/傳輸對(duì)象元素e;
?? 若不存在,則返回false,而且不進(jìn)入隊(duì)列。這是一個(gè)不堵塞的操作。
3. tryTransfer(E e, long timeout, TimeUnit unit)
?? 若當(dāng)前存在一個(gè)正在等待獲取的消費(fèi)者線程,會(huì)馬上傳輸給它; 否則將插入元素e到隊(duì)列尾部,而且等待被消費(fèi)者線程獲取消費(fèi)掉,
?? 若在指定的時(shí)間內(nèi)元素e無(wú)法被消費(fèi)者線程獲取。則返回false,同一時(shí)候該元素被移除。
4. hasWaitingConsumer()
?? 推斷是否存在消費(fèi)者線程
5. getWaitingConsumerCount()
?? 獲取全部等待獲取元素的消費(fèi)線程數(shù)量
事實(shí)上transfer方法在SynchronousQueue的實(shí)現(xiàn)中就已存在了,僅僅是沒有做為API暴露出來(lái)。SynchronousQueue有一個(gè)特性:它本身不存在容量,僅僅能進(jìn)行線程之間的
元素傳送。SynchronousQueue在運(yùn)行offer操作時(shí)。假設(shè)沒有其它線程運(yùn)行poll,則直接返回false.線程之間元素傳送正是通過(guò)transfer方法完畢的。
有一個(gè)使用案例。我們知道ThreadPoolExecutor調(diào)節(jié)線程的原則是:先調(diào)整到最小線程,最小線程用完后,他會(huì)將優(yōu)先將任務(wù)放入緩存隊(duì)列(offer(task)),等緩沖隊(duì)列用完了,才會(huì)向最大線程數(shù)調(diào)節(jié)。這似乎與我們所理解的線程池模型有點(diǎn)不同。我們一般採(cǎi)用添加到最大線程后,才會(huì)放入緩沖隊(duì)列中,以達(dá)到最大性能。
ThreadPoolExecutor代碼段:
? public void execute(Runnable command) {
??????? if (command == null)
??????????? throw new NullPointerException();
??????? if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
??????????? if (runState == RUNNING && workQueue.offer(command)) {
??????????????? if (runState != RUNNING || poolSize == 0)
??????????????????? ensureQueuedTaskHandled(command);
??????????? }
??????????? else if (!addIfUnderMaximumPoolSize(command))
??????????????? reject(command); // is shutdown or saturated
??????? }
??? }
假設(shè)我們採(cǎi)用SynchronousQueue作為ThreadPoolExecuto的緩沖隊(duì)列時(shí),在沒有線程運(yùn)行poll時(shí)(即存在等待線程)。則workQueue.offer(command)返回false,這時(shí)ThreadPoolExecutor就會(huì)添加線程,最快地達(dá)到最大線程數(shù)。
但也僅此而已,也由于SynchronousQueue本身不存在容量,也決定了我們一般無(wú)法採(cǎi)用SynchronousQueue作為ThreadPoolExecutor的緩存隊(duì)列。而一般採(cǎi)用LinkedBlockingQueue的offer方法來(lái)實(shí)現(xiàn)。
最新的LinkedTransferQueue或許能夠幫我們解決問(wèn)題,后面再說(shuō)。
transfer算法比較復(fù)雜,實(shí)現(xiàn)非常難看明確。大致的理解是採(cǎi)用所謂雙重?cái)?shù)據(jù)結(jié)構(gòu)(dual data structures)。之所以叫雙重,其原因是方法都是通過(guò)兩個(gè)步驟完畢:
保留與完畢。比方消費(fèi)者線程從一個(gè)隊(duì)列中取元素,發(fā)現(xiàn)隊(duì)列為空。他就生成一個(gè)空元素放入隊(duì)列,所謂空元素就是數(shù)據(jù)項(xiàng)字段為空。
然后消費(fèi)者線程在這個(gè)字段上旅轉(zhuǎn)等待。這叫保留。直到一個(gè)生產(chǎn)者線程意欲向隊(duì)例中放入一個(gè)元素,這里他發(fā)現(xiàn)最前面的元素的數(shù)據(jù)項(xiàng)字段為NULL,他就直接把自已數(shù)據(jù)填充到這個(gè)元素中。即完畢了元素的傳送。大體是這個(gè)意思。這樣的方式優(yōu)美了完畢了線程之間的高效協(xié)作。
對(duì)于LinkedTransferQueue,Doug Lea進(jìn)行了盡乎極致的優(yōu)化。Grizzly的採(cǎi)用了PaddedAtomicReference:
?? public LinkedTransferQueue() {
??????? QNode dummy = new QNode(null, false);
??????? head = new PaddedAtomicReference<QNode>(dummy);
??????? tail = new PaddedAtomicReference<QNode>(dummy);
??????? cleanMe = new PaddedAtomicReference<QNode>(null);
??? }
?? static final class PaddedAtomicReference<T> extends AtomicReference<T> {??????? // enough padding for 64bytes with 4byte refs
??????? Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe;
??????? PaddedAtomicReference(T r) { super(r); }
??? }
PaddedAtomicReference相對(duì)于父類AtomicReference僅僅做了一件事情,就將共享變量追加到64字節(jié)。我們能夠來(lái)計(jì)算下。一個(gè)對(duì)象的引用占4個(gè)字節(jié),
它追加了15個(gè)變量共占60個(gè)字節(jié),再加上父類的Value變量,一共64個(gè)字節(jié)。這么做的原因。
請(qǐng)參考http://www.infoq.com/cn/articles/ftf-java-volatile
http://rdc.taobao.com/team/jm/archives/1719 這兩文章。做JAVA。假設(shè)想成為Doug Lea這種大師,也要懂體系結(jié)構(gòu)(待續(xù))
原文地址:http://guojuanjun.blog.51cto.com/277646/948298/
版權(quán)聲明:本文博主原創(chuàng)文章。博客,未經(jīng)同意不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的3.集--LinkedTransferQueue得知的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 品牌推广不得不注意的几个误区
- 下一篇: 黑马程序员——iOS学习——启动App界