這篇文章給大家分享的是有關(guān)java集合之ConcurrentLinkedQueue的示例代碼的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
赫山網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)公司成立與2013年到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
(1)ConcurrentLinkedQueue是阻塞隊(duì)列嗎?
(2)ConcurrentLinkedQueue如何保證并發(fā)安全?
(3)ConcurrentLinkedQueue能用于線程池嗎?
ConcurrentLinkedQueue只實(shí)現(xiàn)了Queue接口,并沒有實(shí)現(xiàn)BlockingQueue接口,所以它不是阻塞隊(duì)列,也不能用于線程池中,但是它是線程安全的,可用于多線程環(huán)境中。
那么,它的線程安全又是如何實(shí)現(xiàn)的呢?讓我們一起來瞧一瞧。
// 鏈表頭節(jié)點(diǎn) private transient volatile Nodehead; // 鏈表尾節(jié)點(diǎn) private transient volatile Node tail;
就這兩個(gè)主要屬性,一個(gè)頭節(jié)點(diǎn),一個(gè)尾節(jié)點(diǎn)。
private static class Node{ volatile E item; volatile Node next; }
典型的單鏈表結(jié)構(gòu),非常純粹。
public ConcurrentLinkedQueue() { // 初始化頭尾節(jié)點(diǎn) head = tail = new Node(null); } public ConcurrentLinkedQueue(Collection extends E> c) { Node h = null, t = null; // 遍歷c,并把它元素全部添加到單鏈表中 for (E e : c) { checkNotNull(e); Node newNode = new Node (e); if (h == null) h = t = newNode; else { t.lazySetNext(newNode); t = newNode; } } if (h == null) h = t = new Node (null); head = h; tail = t; }
這兩個(gè)構(gòu)造方法也很簡(jiǎn)單,可以看到這是一個(gè)×××的單鏈表實(shí)現(xiàn)的隊(duì)列。
因?yàn)樗皇亲枞?duì)列,所以只有兩個(gè)入隊(duì)的方法,add(e)和offer(e)。
因?yàn)槭恰痢痢陵?duì)列,所以add(e)方法也不用拋出異常了。
public boolean add(E e) { return offer(e); } public boolean offer(E e) { // 不能添加空元素 checkNotNull(e); // 新節(jié)點(diǎn) final NodenewNode = new Node (e); // 入隊(duì)到鏈表尾 for (Node t = tail, p = t;;) { Node q = p.next; // 如果沒有next,說明到鏈表尾部了,就入隊(duì) if (q == null) { // CAS更新p的next為新節(jié)點(diǎn) // 如果成功了,就返回true // 如果不成功就重新取next重新嘗試 if (p.casNext(null, newNode)) { // 如果p不等于t,說明有其它線程先一步更新tail // 也就不會(huì)走到q==null這個(gè)分支了 // p取到的可能是t后面的值 // 把tail原子更新為新節(jié)點(diǎn) if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. // 返回入隊(duì)成功 return true; } } else if (p == q) // 如果p的next等于p,說明p已經(jīng)被刪除了(已經(jīng)出隊(duì)了) // 重新設(shè)置p的值 p = (t != (t = tail)) ? t : head; else // t后面還有值,重新設(shè)置p的值 p = (p != t && t != (t = tail)) ? t : q; } }
入隊(duì)整個(gè)流程還是比較清晰的,這里有個(gè)前提是出隊(duì)時(shí)會(huì)把出隊(duì)的那個(gè)節(jié)點(diǎn)的next設(shè)置為節(jié)點(diǎn)本身。
(1)定位到鏈表尾部,嘗試把新節(jié)點(diǎn)放到后面;
(2)如果尾部變化了,則重新獲取尾部,再重試;
因?yàn)樗皇亲枞?duì)列,所以只有兩個(gè)出隊(duì)的方法,remove()和poll()。
public E remove() { E x = poll(); if (x != null) return x; else throw new NoSuchElementException(); } public E poll() { restartFromHead: for (;;) { // 嘗試彈出鏈表的頭節(jié)點(diǎn) for (Nodeh = head, p = h, q;;) { E item = p.item; // 如果節(jié)點(diǎn)的值不為空,并且將其更新為null成功了 if (item != null && p.casItem(item, null)) { // 如果頭節(jié)點(diǎn)變了,則不會(huì)走到這個(gè)分支 // 會(huì)先走下面的分支拿到新的頭節(jié)點(diǎn) // 這時(shí)候p就不等于h了,就更新頭節(jié)點(diǎn) // 在updateHead()中會(huì)把head更新為新節(jié)點(diǎn) // 并讓head的next指向其自己 if (p != h) // hop two nodes at a time updateHead(h, ((q = p.next) != null) ? q : p); // 上面的casItem()成功,就可以返回出隊(duì)的元素了 return item; } // 下面三個(gè)分支說明頭節(jié)點(diǎn)變了 // 且p的item肯定為null else if ((q = p.next) == null) { // 如果p的next為空,說明隊(duì)列中沒有元素了 // 更新h為p,也就是空元素的節(jié)點(diǎn) updateHead(h, p); // 返回null return null; } else if (p == q) // 如果p等于p的next,說明p已經(jīng)出隊(duì)了,重試 continue restartFromHead; else // 將p設(shè)置為p的next p = q; } } } // 更新頭節(jié)點(diǎn)的方法 final void updateHead(Node h, Node p) { // 原子更新h為p成功后,延遲更新h的next為它自己 // 這里用延遲更新是安全的,因?yàn)閔ead節(jié)點(diǎn)已經(jīng)變了 // 只要入隊(duì)出隊(duì)的時(shí)候檢查head有沒有變化就行了,跟它的next關(guān)系不大 if (h != p && casHead(h, p)) h.lazySetNext(h); }
出隊(duì)的整個(gè)邏輯也是比較清晰的:
(1)定位到頭節(jié)點(diǎn),嘗試更新其值為null;
(2)如果成功了,就成功出隊(duì);
(3)如果失敗或者頭節(jié)點(diǎn)變化了,就重新尋找頭節(jié)點(diǎn),并重試;
(4)整個(gè)出隊(duì)過程沒有一點(diǎn)阻塞相關(guān)的代碼,所以出隊(duì)的時(shí)候不會(huì)阻塞線程,沒找到元素就返回null;
感謝各位的閱讀!關(guān)于“java集合之ConcurrentLinkedQueue的示例代碼”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!