java中阻塞隊(duì)列和非阻塞隊(duì)列的實(shí)現(xiàn)
在 Java 中,**阻塞隊(duì)列(Blocking Queue)和非阻塞隊(duì)列(Non-Blocking Queue)**是兩種用于并發(fā)編程的隊(duì)列類型,它們?cè)诙嗑€程環(huán)境中有不同的行為和用途。它們的主要區(qū)別在于對(duì)操作的處理方式:阻塞隊(duì)列在操作無(wú)法立即完成時(shí)會(huì)阻塞線程,而非阻塞隊(duì)列則立即返回或進(jìn)行其他操作。
1. 什么是阻塞隊(duì)列?
阻塞隊(duì)列(Blocking Queue)是一種線程安全的隊(duì)列,它在 Java 中位于 java.util.concurrent
包中。阻塞隊(duì)列支持在隊(duì)列為空時(shí)自動(dòng)等待(即阻塞)直到有元素可以消費(fèi),或者在隊(duì)列已滿時(shí)自動(dòng)等待直到有空間可以插入新元素。這種行為使得阻塞隊(duì)列在生產(chǎn)者-消費(fèi)者模型中非常有用。
阻塞隊(duì)列的操作包括阻塞的插入操作和阻塞的刪除操作。它們的主要方法有:
put(E e)
:如果隊(duì)列已滿,當(dāng)前線程將被阻塞,直到隊(duì)列有可用空間。take()
:如果隊(duì)列為空,當(dāng)前線程將被阻塞,直到隊(duì)列中有可用元素。
Java 中常用的阻塞隊(duì)列有以下幾種:
ArrayBlockingQueue
:一個(gè)有界的阻塞隊(duì)列,基于數(shù)組實(shí)現(xiàn)。固定大小,支持公平性設(shè)置。LinkedBlockingQueue
:一個(gè)可選有界的阻塞隊(duì)列,基于鏈表實(shí)現(xiàn)。默認(rèn)大小為Integer.MAX_VALUE
,適用于任務(wù)生產(chǎn)和消費(fèi)速率不同的場(chǎng)景。PriorityBlockingQueue
:一個(gè)支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列,基于堆實(shí)現(xiàn),元素按優(yōu)先級(jí)順序出隊(duì)。DelayQueue
:一個(gè)支持延遲獲取元素的無(wú)界阻塞隊(duì)列,只有在元素的延遲期滿后才能從隊(duì)列中獲取元素。SynchronousQueue
:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,每個(gè)插入操作必須等待一個(gè)相應(yīng)的刪除操作。適用于交換場(chǎng)景。
2. 什么是非阻塞隊(duì)列?
非阻塞隊(duì)列(Non-Blocking Queue)是一種不進(jìn)行阻塞的線程安全隊(duì)列。非阻塞隊(duì)列不等待當(dāng)前線程完成操作,而是立即返回或執(zhí)行其他操作。它們通常使用 CAS(Compare-And-Swap) 操作來(lái)確保線程安全性,從而避免線程在等待鎖時(shí)發(fā)生阻塞。
非阻塞隊(duì)列的操作包括非阻塞的插入操作和非阻塞的刪除操作。它們的主要方法有:
offer(E e)
:嘗試插入元素到隊(duì)列中,如果成功則返回true
,如果隊(duì)列已滿則立即返回false
。poll()
:嘗試從隊(duì)列中取出一個(gè)元素,如果成功則返回元素,如果隊(duì)列為空則立即返回null
。
Java 中常用的非阻塞隊(duì)列有:
ConcurrentLinkedQueue
:一個(gè)基于鏈表的無(wú)界非阻塞隊(duì)列,使用 CAS 操作來(lái)實(shí)現(xiàn)線程安全。適用于高并發(fā)場(chǎng)景。ConcurrentLinkedDeque
:一個(gè)雙端非阻塞隊(duì)列,基于鏈表實(shí)現(xiàn),支持在隊(duì)列的兩端進(jìn)行插入和刪除操作。
3. 阻塞隊(duì)列和非阻塞隊(duì)列的區(qū)別
阻塞與非阻塞:阻塞隊(duì)列在讀取或?qū)懭霑r(shí)可能會(huì)發(fā)生阻塞,而普通隊(duì)列則不會(huì)發(fā)生這種情況。當(dāng)一個(gè)線程嘗試從空的普通隊(duì)列中讀取數(shù)據(jù)或向已滿的普通隊(duì)列中寫(xiě)入數(shù)據(jù)時(shí),它將直接返回一個(gè)特定的值,而不是等待其他線程的操作。
同步與異步:阻塞隊(duì)列通常用于同步處理場(chǎng)景,而普通隊(duì)列通常用于異步處理場(chǎng)景。在同步處理場(chǎng)景下,多個(gè)線程之間需要協(xié)調(diào)工作,而在異步處理場(chǎng)景下,多個(gè)線程之間可以獨(dú)立地進(jìn)行各自的任務(wù)
性能差異:由于阻塞隊(duì)列使用了鎖機(jī)制,所以在高并發(fā)情況下可能會(huì)出現(xiàn)性能瓶頸;而非阻塞隊(duì)列使用了原子操作和CAS指令,因此在高并發(fā)情況下具有更好的性能表現(xiàn)。
4. 使用場(chǎng)景和選擇指南
阻塞隊(duì)列和非阻塞隊(duì)列各自適用于不同的場(chǎng)景。了解它們的特點(diǎn)和工作機(jī)制可以幫助開(kāi)發(fā)者更好地選擇合適的數(shù)據(jù)結(jié)構(gòu)來(lái)解決并發(fā)問(wèn)題。
4.1 阻塞隊(duì)列的使用場(chǎng)景
生產(chǎn)者-消費(fèi)者模型:阻塞隊(duì)列最常見(jiàn)的應(yīng)用場(chǎng)景就是生產(chǎn)者-消費(fèi)者模型。在這種模型中,生產(chǎn)者線程不斷地將任務(wù)放入隊(duì)列,消費(fèi)者線程不斷地從隊(duì)列中取任務(wù)。使用阻塞隊(duì)列可以避免生產(chǎn)者或消費(fèi)者線程在隊(duì)列為空或已滿時(shí)的忙等待,從而提高系統(tǒng)性能。
任務(wù)調(diào)度和工作線程池:在任務(wù)調(diào)度系統(tǒng)或工作線程池中,阻塞隊(duì)列可以用于存放任務(wù)。線程池的工作線程可以從隊(duì)列中取任務(wù)并執(zhí)行,如果沒(méi)有任務(wù)則自動(dòng)等待直到有新任務(wù)到來(lái)。
延遲任務(wù)執(zhí)行:
DelayQueue
適用于需要在一定延遲后執(zhí)行任務(wù)的場(chǎng)景,例如定時(shí)任務(wù)調(diào)度。
4.2 非阻塞隊(duì)列的使用場(chǎng)景
高并發(fā)場(chǎng)景:非阻塞隊(duì)列通常用于需要高并發(fā)訪問(wèn)的場(chǎng)景,因?yàn)樗皇褂面i而是依賴 CAS 操作來(lái)確保線程安全,從而減少了鎖競(jìng)爭(zhēng)的開(kāi)銷,能夠提供更高的吞吐量。
低延遲應(yīng)用:對(duì)于需要快速響應(yīng)、低延遲的應(yīng)用,非阻塞隊(duì)列是非常適合的選擇。例如,在金融交易系統(tǒng)或高性能計(jì)算系統(tǒng)中,需要非??焖俚靥幚碚?qǐng)求而不受鎖的影響。
無(wú)界隊(duì)列:非阻塞隊(duì)列通常是無(wú)界的,例如
ConcurrentLinkedQueue
,這意味著它們不會(huì)限制隊(duì)列大小,但要小心使用,避免內(nèi)存溢出。
5. 阻塞隊(duì)列和非阻塞隊(duì)列的實(shí)現(xiàn)原理
5.1 阻塞隊(duì)列的實(shí)現(xiàn)原理
阻塞隊(duì)列的實(shí)現(xiàn)依賴于內(nèi)部鎖和條件變量(Condition
)來(lái)實(shí)現(xiàn)線程同步。例如,ArrayBlockingQueue
的實(shí)現(xiàn)如下:
- 插入操作(
put()
):如果隊(duì)列已滿,插入線程會(huì)被放入“等待可用空間”的條件隊(duì)列中,直到有其他線程取走元素并喚醒它。 - 取出操作(
take()
):如果隊(duì)列為空,取出線程會(huì)被放入“等待元素”的條件隊(duì)列中,直到有其他線程插入元素并喚醒它。
這些方法通過(guò) ReentrantLock
和 Condition
來(lái)實(shí)現(xiàn)同步控制:
public void put(E e) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); // 等待隊(duì)列非滿 enqueue(e); } finally { lock.unlock(); } }
5.2 非阻塞隊(duì)列的實(shí)現(xiàn)原理
非阻塞隊(duì)列通常使用 CAS(Compare-And-Swap)操作來(lái)實(shí)現(xiàn)線程安全。ConcurrentLinkedQueue
是一個(gè)典型的非阻塞隊(duì)列,它通過(guò)鏈表的方式實(shí)現(xiàn)。其 offer()
和 poll()
方法實(shí)現(xiàn)如下:
插入操作(
offer()
):使用 CAS 操作來(lái)將新節(jié)點(diǎn)插入到鏈表的末尾。如果失敗則不斷重試,直到成功為止。取出操作(
poll()
):使用 CAS 操作來(lái)獲取并移除鏈表的頭節(jié)點(diǎn),同樣會(huì)在操作失敗時(shí)進(jìn)行重試,直到成功。
public boolean offer(E e) { final Node<E> newNode = new Node<>(e); for (Node<E> t = tail, p = t;;) { Node<E> q = p.next; if (q == null) { if (p.casNext(null, newNode)) { if (p != t) casTail(t, newNode); // 使用 CAS 更新尾節(jié)點(diǎn) return true; } } else if (p == q) p = (t != (t = tail)) ? t : head; else p = (p != t && t != (t = tail)) ? t : q; } }
6. 示例代碼
下面是一個(gè)使用阻塞隊(duì)列和非阻塞隊(duì)列的簡(jiǎn)單示例:
阻塞隊(duì)列示例:ArrayBlockingQueue
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class BlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5); // 生產(chǎn)者線程 Thread producer = new Thread(() -> { try { for (int i = 0; i < 10; i++) { System.out.println("生產(chǎn)者生產(chǎn): " + i); queue.put(i); // 阻塞插入操作 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); // 消費(fèi)者線程 Thread consumer = new Thread(() -> { try { while (true) { Integer item = queue.take(); // 阻塞取出操作 System.out.println("消費(fèi)者消費(fèi): " + item); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); producer.start(); consumer.start(); } }
非阻塞隊(duì)列示例:ConcurrentLinkedQueue
import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public class NonBlockingQueueExample { public static void main(String[] args) { Queue<Integer> queue = new ConcurrentLinkedQueue<>(); // 添加元素 queue.offer(1); queue.offer(2); queue.offer(3); // 取出元素 Integer item; while ((item = queue.poll()) != null) { System.out.println("取出: " + item); } } }
7. 總結(jié)
阻塞隊(duì)列和非阻塞隊(duì)列在 Java 并發(fā)編程中具有不同的應(yīng)用場(chǎng)景和特點(diǎn)。阻塞隊(duì)列通過(guò)內(nèi)部鎖和條件變量實(shí)現(xiàn)線程安全,適用于生產(chǎn)者-消費(fèi)者模型和任務(wù)調(diào)度等場(chǎng)景。非阻塞隊(duì)列通過(guò) CAS 操作實(shí)現(xiàn)線程安全,適用于高并發(fā)和低延遲場(chǎng)景。
到此這篇關(guān)于java中阻塞隊(duì)列和非阻塞隊(duì)列的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)java 阻塞隊(duì)列和非阻塞隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JavaEE多線程中阻塞隊(duì)列的項(xiàng)目實(shí)踐
- Java的PriorityBlockingQueue優(yōu)先級(jí)阻塞隊(duì)列代碼實(shí)例
- Java中的SynchronousQueue阻塞隊(duì)列使用代碼實(shí)例
- Java中的SynchronousQueue阻塞隊(duì)列及使用場(chǎng)景解析
- java中的BlockingQueue(阻塞隊(duì)列)解析
- Java中的BlockingQueue阻塞隊(duì)列原理以及實(shí)現(xiàn)詳解
- Java的非阻塞隊(duì)列ConcurrentLinkedQueue解讀
- Java多線程實(shí)現(xiàn)阻塞隊(duì)列的示例代碼
相關(guān)文章
SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例
這篇文章主要介紹了SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03全解史上最快的JOSN解析庫(kù)alibaba Fastjson
這篇文章主要介紹了史上最快的JOSN解析庫(kù)alibaba Fastjson,對(duì)FastJson感興趣的同學(xué),一定要看一下2021-04-04Spring Native項(xiàng)目實(shí)戰(zhàn)(體驗(yàn)79毫秒啟動(dòng)springboot應(yīng)用)
Spring Native是Spring提供的、制作native image的技術(shù)方案,本篇主要內(nèi)容是開(kāi)發(fā)springboot應(yīng)用再構(gòu)建為native image的方法,通過(guò)Spring Native項(xiàng)目實(shí)戰(zhàn)讓大家體驗(yàn)79毫秒啟動(dòng)springboot應(yīng)用,感興趣的朋友跟隨小編一起看看吧2021-05-05Spring注解 TX聲明式事務(wù)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Spring注解 - TX 聲明式事務(wù)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04