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

Java線程池隊列PriorityBlockingQueue原理分析

 更新時間:2023年12月18日 08:32:50   作者:Brycen?Liu  
這篇文章主要介紹了Java線程池隊列PriorityBlockingQueue原理分析,PriorityBlockingQueue隊列是?JDK1.5?的時候出來的一個阻塞隊列,但是該隊列入隊的時候是不會阻塞的,永遠會加到隊尾,需要的朋友可以參考下

一、什么是PriorityBlockingQueue?

PriorityBlockingQueue隊列是 JDK1.5 的時候出來的一個阻塞隊列。但是該隊列入隊的時候是不會阻塞的,永遠會加到隊尾。下面我們介紹下它的幾個特點:

  • PriorityBlockingQueue 和 ArrayBlockingQueue 一樣是基于數(shù)組實現(xiàn)的,但后者在初始化時需要指定長度,前者默認長度是 11。
  • 該隊列可以說是真正的無界隊列,它在隊列滿的時候會進行擴容,而前面說的無界阻塞隊列其實都有有界,只是界限太大可以忽略(最大值是 2147483647)
  • 該隊列屬于權(quán)重隊列,可以理解為它可以進行排序,但是排序不是從小到大排或從大到小排,是基于數(shù)組的堆結(jié)構(gòu)(具體如何排下面會進行分析)
  • 出隊方式和前面的也不同,是根據(jù)權(quán)重來進行出隊,和前面所說隊列中那種先進先出或者先進后出方式不同。
  • 其存入的元素必須實現(xiàn)Comparator,或者在創(chuàng)建隊列的時候自定義Comparator

注意:

  1. 堆結(jié)構(gòu)實際上是一種完全二叉樹,建議學習前了解一下二叉樹。
  2. 堆又分為大頂堆小頂堆。大頂堆中第一個元素肯定是所有元素中最大的,小頂堆中第一個元素是所有元素中最小的。

二、PriorityBlockingQueue類圖:

在這里插入圖片描述

三、源碼解析

1、字段講解

從下面的字段我們可以知道,該隊列可以排序,使用顯示鎖來保證操作的原子性,在空隊列時,出隊線程會堵塞等。

/**
	* 默認數(shù)組長度
	*/
	private static final int DEFAULT_INITIAL_CAPACITY = 11;
    /**
	 * 最大達容量,分配時超出可能會出現(xiàn) OutOfMemoryError 異常
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    /**
	 * 隊列,存儲我們的元素
     */
    private transient Object[] queue;
    /**
     * 隊列長度
     */
    private transient int size;
    /**
     * 比較器,入隊進行權(quán)重的比較
     */
    private transient Comparator<? super E> comparator;
    /**
     * 顯示鎖
     */
    private final ReentrantLock lock;
    /**
     * 空隊列時進行線程阻塞的 Condition 對象
     */
    private final Condition notEmpty;

2、構(gòu)造方法

/**
	* 默認構(gòu)造,使用長度為 11 的數(shù)組,比較器為空
	*/
 	public PriorityBlockingQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
	/**
	* 自定義數(shù)據(jù)長度構(gòu)造,比較器為空
	*/
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    }
	/**
	* 自定義數(shù)組長度,可以自定義比較器
	*/
    public PriorityBlockingQueue(int initialCapacity,
                                 Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.comparator = comparator;
        this.queue = new Object[initialCapacity];
    }

3、入隊方法

3.1、put方法

入隊方法,下面可以看到 put 方法最終會調(diào)用 offer 方法,所以我們只看 offer 方法即可。

    public void put(E e) {
        offer(e); // never need to block
    }
 	public boolean offer(E e) {
 		//判斷是否為空
        if (e == null)
            throw new NullPointerException();
       	//顯示鎖
        final ReentrantLock lock = this.lock;
        lock.lock();
        //定義臨時對象
        int n, cap;
        Object[] array;
        //判斷數(shù)組是否滿了
        while ((n = size) >= (cap = (array = queue).length))
        	//數(shù)組擴容
            tryGrow(array, cap);
        try {
        	//拿到比較器
            Comparator<? super E> cmp = comparator;
            //判斷是否有自定義比較器
            if (cmp == null)
            	//堆上浮
                siftUpComparable(n, e, array);
            else
            	//使用自定義比較器進行堆上浮
                siftUpUsingComparator(n, e, array, cmp);
            //隊列長度 +1
            size = n + 1;
            //喚醒休眠的出隊線程
            notEmpty.signal();
        } finally {
        	//釋放鎖
            lock.unlock();
        }
        return true;
    }

3.2、上浮調(diào)整比較器方法的實現(xiàn)

private static <T> void siftUpComparable(int k, T x, Object[] array) {
        Comparable<? super T> key = (Comparable<? super T>) x;
        while (k > 0) {
        	//無符號向左移,目的是找到放入位置的父節(jié)點
            int parent = (k - 1) >>> 1;
            //拿到父節(jié)點的值
            Object e = array[parent];
            //比較是否大于該元素,不大于就沒比較交換
            if (key.compareTo((T) e) >= 0)
                break;
            //以下都是元素位置交換
            array[k] = e;
            k = parent;
        }
        array[k] = key;
    }

根據(jù)上面的代碼,可以看出這是完全二叉樹在進行上浮調(diào)整。調(diào)整入隊的元素,找出最小的,將元素排列有序化。簡單理解就是:父節(jié)點元素值一定要比它的子節(jié)點得小,如果父節(jié)點大于子節(jié)點了,那就兩者位置進行交換。

3.3、入隊圖解

說的可能很模糊,我們先寫個 demo,根據(jù) demo 來進行圖解分析:

/**
 * @Auther: Gentle
 * @Date: 2019/4/14 15:11
 * @Description: PriorityBlockingQueue 簡單演示 demo 
 */
public class TestPriorityBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue<Integer> concurrentLinkedQueue = new PriorityBlockingQueue<Integer>();
        concurrentLinkedQueue.offer(10);
        concurrentLinkedQueue.offer(20);
        concurrentLinkedQueue.offer(5);
        concurrentLinkedQueue.offer(1);
        concurrentLinkedQueue.offer(25);
        concurrentLinkedQueue.offer(30);
        //輸出元素排列
        concurrentLinkedQueue.stream().forEach(e-> System.out.print(e+"  "));
        //取出元素
		Integer take = concurrentLinkedQueue.take();
        System.out.println();
        concurrentLinkedQueue.stream().forEach(e-> System.out.print(e+"  "));
    }
}

上面可以看出,我們要入隊的元素是 [10,20,5,1,21,30],接下來我們用圖來演示一步步入隊情況。

隊列初始化時:

在這里插入圖片描述

這時,我們開始將元素 元素 10 入隊,并用二叉樹輔助理解:

在這里插入圖片描述

我們在將元素 20 入隊:

在這里插入圖片描述

將元素 5 入隊后發(fā)現(xiàn)父節(jié)點大于子節(jié)點,這時需要進行上浮調(diào)整

在這里插入圖片描述

開始進行上浮調(diào)整,將元素 10 和元素 5進行位置調(diào)換,結(jié)果如下:

在這里插入圖片描述

接著將元素 1 入隊后發(fā)現(xiàn)父節(jié)點大于子節(jié)點,繼續(xù)進行調(diào)整:

在這里插入圖片描述

第一次調(diào)整將元素 20 和元素 1 進行位置交換,交換完畢后結(jié)果如下:

在這里插入圖片描述

交換完畢后,我們發(fā)現(xiàn)父節(jié)點的元素值還是大于子節(jié)點,說明還需要進行一次交換,最后交換結(jié)果如下:

在這里插入圖片描述

接下來將元素 25 和 30 入隊,結(jié)果如下:

在這里插入圖片描述

注: 最小堆的的頂端一定是元素值最小的那個。

4、出隊方法

4.1、take方法

出隊方法,該方法會阻塞

public E take() throws InterruptedException {
	//顯示鎖
    final ReentrantLock lock = this.lock;
    //可中斷鎖
    lock.lockInterruptibly();
    //結(jié)果接受對象
    E result;
    try {
    	//判讀隊列是否為空
        while ( (result = dequeue()) == null)
        	//線程阻塞
            notEmpty.await();
    } finally {
        lock.unlock();
    }
    return result;
}

4.2、dequeue方法

具體出隊方法的實現(xiàn)

 private E dequeue() {
	//長度減少 1
    int n = size - 1;
    //判斷隊列中是否又元素
    if (n < 0)
        return null;
    else {
    	//隊列對象
        Object[] array = queue;
        //取出第一個元素
        E result = (E) array[0];
        //拿出最后一個元素
        E x = (E) array[n];
        //置空
        array[n] = null;
        Comparator<? super E> cmp = comparator;
        if (cmp == null)
        	//下沉調(diào)整
            siftDownComparable(0, x, array, n);
        else
            siftDownUsingComparator(0, x, array, n, cmp);
       	//成功則減少隊列中的元素數(shù)量
        size = n;
        return result;
    }
}

總體就是找到父節(jié)點與兩個子節(jié)點中最小的一個節(jié)點,然后進行交換位置,不斷重復,由上而下的交換。

4.3、下沉調(diào)整比較器方法的實現(xiàn)

private static <T> void siftDownComparable(int k, T x, Object[] array,
                                               int n) {
    //判斷隊列長度
    if (n > 0) {
        Comparable<? super T> key = (Comparable<? super T>)x;
        //找到隊列最后一個元素的父節(jié)點的索引。
        //如下圖最大元素是30 父節(jié)點是 10,對于索引是 2
        int half = n >>> 1;           // loop while a non-leaf
        while (k < half) {
        	//拿到 k 節(jié)點下的左子節(jié)點
            int child = (k << 1) + 1; // assume left child is least
            //取得子節(jié)點對應的值
            Object c = array[child];
            //取得 k 右子節(jié)點的索引
            int right = child + 1;
            //比較右節(jié)點的索引是否小于隊列長度和左右子節(jié)點的值進行比較
            if (right < n &&
                ((Comparable<? super T>) c).compareTo((T) array[right]) > 0)
                c = array[child = right];
            //比較父節(jié)點值是否大于子節(jié)點
            if (key.compareTo((T) c) <= 0)
                break;
            //下面都是元素替換
            array[k] = c;
            k = child;
        }
        array[k] = key;
    }
}

4.4、出隊圖解

在這里插入圖片描述

這時,我們需要從隊列中取出第一個元素 1,元素 1 取出時會與隊列中最后一個元素進行交換,并將最后一個元素置空。(實際上源碼不是這么做的,源代碼中是用變量來保存索引,直到全部下沉調(diào)整完成才進行替換)

在這里插入圖片描述

替換后,結(jié)果就如下圖顯示一樣。我們發(fā)現(xiàn)父節(jié)點大于子節(jié)點了,所以還需要再一次進行替換操作。 在這里插入圖片描述再一次替換后,將元素 30 下沉到下一個左邊子節(jié)點,子節(jié)點上浮到原父節(jié)點位置。這就完成了下沉調(diào)整了。

在這里插入圖片描述

四、總結(jié)

PriorityBlockingQueue 真的是個神奇的隊列,可以實現(xiàn)優(yōu)先出隊。最特別的是它只有一個鎖,入隊操作永遠成功,而出隊只有在空隊列的時候才會進行線程阻塞??梢哉f有一定的應用場景吧,比如:有任務要執(zhí)行,可以對任務加一個優(yōu)先級的權(quán)重,這樣隊列會識別出來,對該任務優(yōu)先進行出隊。

到此這篇關(guān)于Java線程池隊列PriorityBlockingQueue原理分析的文章就介紹到這了,更多相關(guān)Java的PriorityBlockingQueue內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中的SynchronousQueue阻塞隊列使用代碼實例

    Java中的SynchronousQueue阻塞隊列使用代碼實例

    這篇文章主要介紹了Java中的SynchronousQueue阻塞隊列使用代碼實例,SynchronousQueue是無緩沖區(qū)的阻塞隊列,即不能直接向隊列中添加數(shù)據(jù),會報隊列滿異常,需要的朋友可以參考下
    2023-12-12
  • SpringBoot框架aop切面的execution表達式解讀

    SpringBoot框架aop切面的execution表達式解讀

    這篇文章主要介紹了SpringBoot框架aop切面的execution表達式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Spark調(diào)優(yōu)多線程并行處理任務實現(xiàn)方式

    Spark調(diào)優(yōu)多線程并行處理任務實現(xiàn)方式

    這篇文章主要介紹了Spark調(diào)優(yōu)多線程并行處理任務實現(xiàn)方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot指標監(jiān)控的實現(xiàn)

    SpringBoot指標監(jiān)控的實現(xiàn)

    本文主要介紹了SpringBoot指標監(jiān)控的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • spring-cloud入門之spring-cloud-config(配置中心)

    spring-cloud入門之spring-cloud-config(配置中心)

    這篇文章主要介紹了spring-cloud入門之spring-cloud-config(配置中心),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • springboot+dubbo啟動項目時報錯 zookeeper not connected的問題及解決方案

    springboot+dubbo啟動項目時報錯 zookeeper not connect

    這篇文章主要介紹了springboot+dubbo項目啟動項目時報錯 zookeeper not connected的問題,本文給大家定位問題及解決方案,結(jié)合實例代碼給大家講解的非常詳細,需要的朋友可以參考下
    2023-06-06
  • Java String轉(zhuǎn)換時為null的解決方法

    Java String轉(zhuǎn)換時為null的解決方法

    這篇文章主要介紹了Java String轉(zhuǎn)換時為null的解決方法,需要的朋友可以參考下
    2017-07-07
  • java實現(xiàn)輕量型http代理服務器示例

    java實現(xiàn)輕量型http代理服務器示例

    這篇文章主要介紹了java實現(xiàn)輕量型http代理服務器示例,需要的朋友可以參考下
    2014-04-04
  • Mybatis中OGNL表達式的具體使用

    Mybatis中OGNL表達式的具體使用

    OGNL是一種簡潔、靈活的表達式語言,本文就來介紹一下OGNL的概念以及在Mybatis中的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-12-12
  • 使用javafx更新UI的方法

    使用javafx更新UI的方法

    這篇文章主要介紹了使用javafx更新UI的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論

江孜县| 锡林浩特市| 平安县| 贵阳市| 曲周县| 罗田县| 苏尼特左旗| 泰和县| 治县。| 专栏| 老河口市| 区。| 伊吾县| 大荔县| 无极县| 九江县| 汝南县| 隆昌县| 福泉市| 娱乐| 淳安县| 桦甸市| 晋中市| 贞丰县| 花垣县| 金门县| 沂水县| 红安县| 清徐县| 明水县| 特克斯县| 和硕县| 子长县| 松江区| 宽城| 新丰县| 哈尔滨市| 泾阳县| 阿拉善左旗| 西宁市| 凤城市|