最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的LinkedBlockingQueue源碼解析

 更新時間:2023年12月26日 10:52:15   作者:茫然背影  
這篇文章主要介紹了Java中的LinkedBlockingQueue源碼解析,LinkedBlockingQueue底層是一個鏈表(可以指定容量,默認(rèn)是Integer.MAX_VALUE),維持了兩把鎖,一把鎖用于入隊,一把鎖用于出隊,并且使用一個AtomicInterger類型的變量保證線程安全,需要的朋友可以參考下

基本認(rèn)識

LinkedBlockingQueue可以指定容量,內(nèi)部維持一個隊列,所以有一個頭節(jié)點(diǎn)head和一個尾節(jié)點(diǎn)last,內(nèi)部維持兩把鎖,一個用于入隊,一個用于出隊,還有鎖關(guān)聯(lián)的Condition對象。主要對象的定義如下:

    //容量,如果沒有指定,該值為Integer.MAX_VALUE;
    private final int capacity;
    //當(dāng)前隊列中的元素
    private final AtomicInteger count = new AtomicInteger();
    //隊列頭節(jié)點(diǎn),始終滿足head.item==null
    transient Node<E> head;
    //隊列的尾節(jié)點(diǎn),始終滿足last.next==null
    private transient Node<E> last;
    //用于出隊的鎖
    private final ReentrantLock takeLock = new ReentrantLock();
    //當(dāng)隊列為空時,保存執(zhí)行出隊的線程
    private final Condition notEmpty = takeLock.newCondition();
    //用于入隊的鎖
    private final ReentrantLock putLock = new ReentrantLock();
    //當(dāng)隊列滿時,保存執(zhí)行入隊的線程
    private final Condition notFull = putLock.newCondition();

構(gòu)造方法

LinkedBlockingQueue的構(gòu)造方法有三個,分別如下:

從構(gòu)造方法中可以得出3點(diǎn)結(jié)論: 

1. 當(dāng)調(diào)用無參的構(gòu)造方法時,容量是int的最大值 

2. 隊列中至少包含一個節(jié)點(diǎn),哪怕隊列對外表現(xiàn)為空 

3. LinkedBlockingQueue不支持null元素

 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);//last和head在隊列為空時都存在,所以隊列中至少有一個節(jié)點(diǎn)
    }
    public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }

put(E e)方法

首先獲得入隊的鎖putLock,判斷隊列是否已滿:count == capacity

  • 未滿:將節(jié)點(diǎn)鏈入尾部,元素數(shù)量+1,此時如果發(fā)現(xiàn)隊列還沒滿還可以生產(chǎn),就喚醒其他生產(chǎn)線程notFull.signal也進(jìn)行生產(chǎn),生產(chǎn)一個后,如果此時隊列是有空變?yōu)榉强盏模ㄗC明此時所有的消費(fèi)者都在阻塞notEmpty.await(),此時必須由生產(chǎn)者進(jìn)行喚醒,不然無法向下進(jìn)行,也就是從空到非空這個時候必須由生產(chǎn)者喚醒消費(fèi)者,之后的就是消費(fèi)者喚醒自己的兄弟姐妹們),就喚醒消費(fèi)者隊列notEmpty的頭結(jié)點(diǎn)notEmpty.signal,通知消費(fèi)這消費(fèi)這個消費(fèi)者消費(fèi)后發(fā)現(xiàn)還有可以消費(fèi)的元素,就通知notEmpty隊列里的頭結(jié)點(diǎn),就這樣notEmpty隊列一次被喚醒了,notEmpty只在第一次是被生產(chǎn)者喚醒的。
  • 已滿:就調(diào)用notFull.await阻塞,釋放鎖,從AQS隊列移除,將生產(chǎn)者加入到notFull條件隊列尾部,等待著被喚醒后繼續(xù)生產(chǎn)
public void put(E e) throws InterruptedException {
        //不允許元素為null
        if (e == null) throw new NullPointerException();
        int c = -1;
        //以當(dāng)前元素新建一個節(jié)點(diǎn)
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        //獲得入隊的鎖
        putLock.lockInterruptibly();
        try {
            //如果隊列已滿,那么將該線程加入到Condition的等待隊列中
            while (count.get() == capacity) {
                notFull.await();
            }
            //將節(jié)點(diǎn)入隊
            enqueue(node);
            //得到插入之前隊列的元素個數(shù)
            c = count.getAndIncrement();
            //如果還可以插入元素,那么釋放等待的入隊線程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            //解鎖
            putLock.unlock();
        }
        //通知出隊線程隊列非空
        if (c == 0)
            signalNotEmpty();
    }
 private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        //獲取takeLock
        takeLock.lock();
        try {
            //釋放notEmpty條件隊列中的第一個等待線程
            notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
    }

E take()方法

首先獲取takeLodck,判斷隊列是否可以消費(fèi):count.get() == 0

  • 可以消費(fèi):返回頭節(jié)點(diǎn)的下一個節(jié)點(diǎn)(頭結(jié)點(diǎn)不存數(shù)據(jù),是永遠(yuǎn)存在的一個節(jié)點(diǎn)),并移除此節(jié)點(diǎn),元素數(shù)量-1,如果此時發(fā)現(xiàn)隊列中還有元素可以消費(fèi),就喚醒其他消費(fèi)者notEmpty.signal,進(jìn)行消費(fèi)。如果此時隊列是從滿變?yōu)槲礉M的(證明此時所有的生產(chǎn)者都在阻塞,此時必須有消費(fèi)者喚醒生產(chǎn)者的第一個節(jié)點(diǎn),之后就是生產(chǎn)者喚醒自己的兄弟姐妹了),
  • 不可以消費(fèi):調(diào)用notEmpty.awati進(jìn)行阻塞,釋放鎖,從AQS隊列移除,進(jìn)入NotEmpty隊列尾部,等待被喚醒
public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        //獲取takeLock鎖
        takeLock.lockInterruptibly();
        try {
            //如果隊列為空,那么加入到notEmpty條件的等待隊列中
            while (count.get() == 0) {
                notEmpty.await();
            }
            //得到隊頭元素
            x = dequeue();
            //得到取走一個元素之前隊列的元素個數(shù)
            c = count.getAndDecrement();
            //如果隊列中還有數(shù)據(jù)可取,釋放notEmpty條件等待隊列中的第一個線程
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        //如果隊列中的元素從滿到非滿,通知put線程
        if (c == capacity)
            signalNotFull();
        return x;
    }
private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

remove()方法

remove(Object)操作會從隊列的頭遍歷到尾,用到了隊列的兩端,所以需要對兩端加鎖,而對兩端加鎖就需要獲取兩把鎖;

remove()是從頭結(jié)點(diǎn)刪除,所以這個方法只需要獲取take鎖。

 public boolean remove(Object o) {
        //因為隊列不包含null元素,返回false
        if (o == null) return false;
        //獲取兩把鎖
        fullyLock();
        try {
            //從頭的下一個節(jié)點(diǎn)開始遍歷
            for (Node<E> trail = head, p = trail.next;
                 p != null;
                 trail = p, p = p.next) {
                 //如果匹配,那么將節(jié)點(diǎn)從隊列中移除,trail表示前驅(qū)節(jié)點(diǎn)
                if (o.equals(p.item)) {
                    unlink(p, trail);
                    return true;
                }
            }
            return false;
        } finally {
            //釋放兩把鎖
            fullyUnlock();
        }
    }

size()方法

由于count是一個AtomicInteger的變量,所以該方法是一個原子性的操作,是線程安全的。

public int size() {
        return count.get();
    }

LinkedBlockingDeque

從上面的字段,可以得到LinkedBlockingDeque內(nèi)部只有一把鎖以及該鎖上關(guān)聯(lián)的兩個條件,同一時刻只有一個線程可以在隊頭或者隊尾執(zhí)行入隊或出隊操作??梢园l(fā)現(xiàn)這點(diǎn)和LinkedBlockingQueue不同,LinkedBlockingQueue可以同時有兩個線程在兩端執(zhí)行操作。  

LinkedBlockingQueue實現(xiàn)總結(jié)

LinkedBlockingQueue底層是一個鏈表(可以指定容量,默認(rèn)是Integer.MAX_VALUE),維持了兩把鎖,一把鎖用于入隊,一把鎖用于出隊,并且使用一個AtomicInterger類型的變量保證線程安全,AtomicInterger:表示當(dāng)前隊列中含有的元素個數(shù):

  • 生產(chǎn)者不斷進(jìn)行生產(chǎn)會向鏈表尾部不斷鏈入元素,直到達(dá)到容量后,此時所有生產(chǎn)者依次進(jìn)入notFull條件隊列進(jìn)行阻塞,此時如果任意一個消費(fèi)者消費(fèi)了一個元素,就會通知notFull隊列第一個節(jié)點(diǎn)進(jìn)行生產(chǎn),notFull第一個節(jié)點(diǎn)生產(chǎn)完畢后發(fā)現(xiàn)還有位置可以生產(chǎn)就會喚醒notFull的第二個節(jié)點(diǎn),notFull第二個節(jié)點(diǎn)生產(chǎn)后發(fā)現(xiàn)還有位置則喚醒notFull第三個節(jié)點(diǎn),就這樣就可以喚醒notFull里的所有生產(chǎn)者
  • 消費(fèi)從鏈表頭部開始向后消費(fèi),只要還有元素就可以不斷消費(fèi),消費(fèi)完所有的元素后,此時所有消費(fèi)者依次進(jìn)入notEmpty條件隊列進(jìn)行阻塞, 這個時候一旦生產(chǎn)者生產(chǎn)了一個元素,就會喚醒notEmpty的第一個節(jié)點(diǎn),而這個節(jié)點(diǎn)消費(fèi)完后如果發(fā)現(xiàn)還有元素可以消費(fèi),就會喚醒自己的兄弟姐妹(notEmpty的第二個節(jié)點(diǎn)),notEmpty的第二個節(jié)點(diǎn)消費(fèi)完后如果發(fā)現(xiàn)還有元素可以消費(fèi)就會再喚醒notEmpty的第三個節(jié)點(diǎn),就這樣就喚醒了notEmpty里的所有的消費(fèi)者
  • 消費(fèi)者一直在砍頭,生產(chǎn)者一直在添尾

到此這篇關(guān)于Java中的LinkedBlockingQueue源碼解析的文章就介紹到這了,更多相關(guān)LinkedBlockingQueue源碼解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot項目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問題解決

    springboot項目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問題解決

    這篇文章主要介紹了springboot項目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問題解決,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-12-12
  • Spring Boot中使用jdbctemplate 操作MYSQL數(shù)據(jù)庫實例

    Spring Boot中使用jdbctemplate 操作MYSQL數(shù)據(jù)庫實例

    本篇文章主要介紹了Spring Boot中使用jdbctemplate 操作MYSQL數(shù)據(jù)庫實例,具有一定的參考價值,有興趣的可以了解一下。
    2017-04-04
  • 詳解mybatis-plus使用@EnumValue注解的方式對枚舉類型的處理

    詳解mybatis-plus使用@EnumValue注解的方式對枚舉類型的處理

    這篇文章主要介紹了詳解mybatis-plus使用@EnumValue注解的方式對枚舉類型的處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java縮略圖生成庫之Thumbnailator應(yīng)用說明

    Java縮略圖生成庫之Thumbnailator應(yīng)用說明

    Thumbnailator是一個為Java界面更流暢的縮略圖生成庫,從API提供現(xiàn)有的圖像文件和圖像對象的縮略圖中簡化了縮略過程,兩三行代碼就能夠從現(xiàn)有圖片生成縮略圖,使用起來非常方便,需要的朋友可以了解下
    2012-12-12
  • Java哈希表和有序表實例代碼講解

    Java哈希表和有序表實例代碼講解

    這篇文章主要介紹了Java哈希表和有序表,哈希表也稱散列表,是一種以鍵值對形式存儲記錄的數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)支持根據(jù)鍵的內(nèi)容直接訪問在內(nèi)存特定位置的值,并且可以進(jìn)行查找、添加和刪除操作
    2023-04-04
  • 使用jd-gui反編譯修改jar包里的.class并重新生成新jar問題

    使用jd-gui反編譯修改jar包里的.class并重新生成新jar問題

    這篇文章主要介紹了使用jd-gui反編譯修改jar包里的.class并重新生成新jar問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用Java實現(xiàn)將多聲道音頻轉(zhuǎn)換為單聲道文件的轉(zhuǎn)換方案

    使用Java實現(xiàn)將多聲道音頻轉(zhuǎn)換為單聲道文件的轉(zhuǎn)換方案

    在音頻處理領(lǐng)域,開發(fā)者經(jīng)常需要對錄音文件進(jìn)行聲道數(shù)調(diào)整,例如從雙聲道轉(zhuǎn)換為單聲道,以便減小文件體積、適配語音識別模型或統(tǒng)一音頻輸入格式,許多項目會直接依賴FFmpeg,但這會引入外部依賴、部署復(fù)雜性和跨平臺兼容問題,本文將介紹一種 純Java實現(xiàn)的音頻聲道轉(zhuǎn)換方案
    2025-11-11
  • 如何使用lamda表達(dá)式對list進(jìn)行求和

    如何使用lamda表達(dá)式對list進(jìn)行求和

    這篇文章主要介紹了如何使用lamda表達(dá)式對list進(jìn)行求和問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Spring Data Jpa實現(xiàn)分頁和排序代碼實例

    Spring Data Jpa實現(xiàn)分頁和排序代碼實例

    本篇文章主要介紹了Spring Data Jpa實現(xiàn)分頁和排序代碼實例,具有一定的參考價值,有興趣的可以了解一下。
    2017-03-03
  • IntelliJ IDEA 詳細(xì)圖解最常用的配置(適合剛剛用的新人)

    IntelliJ IDEA 詳細(xì)圖解最常用的配置(適合剛剛用的新人)

    這篇文章主要介紹了IntelliJ IDEA 詳細(xì)圖解最常用的配置,本篇教程非常適合剛剛用的新人,本文圖文并茂給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08

最新評論

江安县| 陕西省| 大渡口区| 玛沁县| 中宁县| 道孚县| 三台县| 荆州市| 潞西市| 大悟县| 奉节县| 建湖县| 司法| 钟祥市| 师宗县| 临沭县| 乌拉特前旗| 全南县| 兴安盟| 泰和县| 水富县| 上虞市| 广水市| 浮梁县| 孟州市| 慈利县| 屯门区| 册亨县| 区。| 台北县| 娄烦县| 门头沟区| 望都县| 保德县| 仁怀市| 瑞金市| 开化县| 包头市| 札达县| 革吉县| 屏南县|