亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java線程池隊(duì)列PriorityBlockingQueue原理分析

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

一、什么是PriorityBlockingQueue?

PriorityBlockingQueue隊(duì)列是 JDK1.5 的時(shí)候出來(lái)的一個(gè)阻塞隊(duì)列。但是該隊(duì)列入隊(duì)的時(shí)候是不會(huì)阻塞的,永遠(yuǎn)會(huì)加到隊(duì)尾。下面我們介紹下它的幾個(gè)特點(diǎn):

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

注意:

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

二、PriorityBlockingQueue類(lèi)圖:

在這里插入圖片描述

三、源碼解析

1、字段講解

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

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

2、構(gòu)造方法

/**
	* 默認(rèn)構(gòu)造,使用長(zhǎng)度為 11 的數(shù)組,比較器為空
	*/
 	public PriorityBlockingQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
	/**
	* 自定義數(shù)據(jù)長(zhǎng)度構(gòu)造,比較器為空
	*/
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    }
	/**
	* 自定義數(shù)組長(zhǎng)度,可以自定義比較器
	*/
    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、入隊(duì)方法

3.1、put方法

入隊(duì)方法,下面可以看到 put 方法最終會(huì)調(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();
        //定義臨時(shí)對(duì)象
        int n, cap;
        Object[] array;
        //判斷數(shù)組是否滿了
        while ((n = size) >= (cap = (array = queue).length))
        	//數(shù)組擴(kuò)容
            tryGrow(array, cap);
        try {
        	//拿到比較器
            Comparator<? super E> cmp = comparator;
            //判斷是否有自定義比較器
            if (cmp == null)
            	//堆上浮
                siftUpComparable(n, e, array);
            else
            	//使用自定義比較器進(jìn)行堆上浮
                siftUpUsingComparator(n, e, array, cmp);
            //隊(duì)列長(zhǎng)度 +1
            size = n + 1;
            //喚醒休眠的出隊(duì)線程
            notEmpty.signal();
        } finally {
        	//釋放鎖
            lock.unlock();
        }
        return true;
    }

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

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

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

3.3、入隊(duì)圖解

說(shuō)的可能很模糊,我們先寫(xiě)個(gè) demo,根據(jù) demo 來(lái)進(jìn)行圖解分析:

/**
 * @Auther: Gentle
 * @Date: 2019/4/14 15:11
 * @Description: PriorityBlockingQueue 簡(jiǎn)單演示 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+"  "));
    }
}

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

隊(duì)列初始化時(shí):

在這里插入圖片描述

這時(shí),我們開(kāi)始將元素 元素 10 入隊(duì),并用二叉樹(shù)輔助理解:

在這里插入圖片描述

我們?cè)趯⒃?20 入隊(duì):

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

4、出隊(duì)方法

4.1、take方法

出隊(duì)方法,該方法會(huì)阻塞

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

4.2、dequeue方法

具體出隊(duì)方法的實(shí)現(xiàn)

 private E dequeue() {
	//長(zhǎng)度減少 1
    int n = size - 1;
    //判斷隊(duì)列中是否又元素
    if (n < 0)
        return null;
    else {
    	//隊(duì)列對(duì)象
        Object[] array = queue;
        //取出第一個(gè)元素
        E result = (E) array[0];
        //拿出最后一個(gè)元素
        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);
       	//成功則減少隊(duì)列中的元素?cái)?shù)量
        size = n;
        return result;
    }
}

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

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

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

4.4、出隊(duì)圖解

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

四、總結(jié)

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

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

相關(guān)文章

  • Spring整合Junit的使用詳解

    Spring整合Junit的使用詳解

    這篇文章主要介紹了Spring整合Junit的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • springboot集成mybatis官方生成器

    springboot集成mybatis官方生成器

    本文主要介紹了springboot集成mybatis官方生成器,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java實(shí)現(xiàn)的生成二維碼和解析二維碼URL操作示例

    Java實(shí)現(xiàn)的生成二維碼和解析二維碼URL操作示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的生成二維碼和解析二維碼URL操作,結(jié)合實(shí)例形式分析了Java創(chuàng)建與解析二維碼,以及文件讀寫(xiě)等相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • Java深入淺出數(shù)組的定義與使用下篇

    Java深入淺出數(shù)組的定義與使用下篇

    數(shù)組是有序的元素序列,若將有限個(gè)類(lèi)型相同的變量的集合命名,那么這個(gè)名稱(chēng)為數(shù)組名。組成數(shù)組的各個(gè)變量稱(chēng)為數(shù)組的分量,也稱(chēng)為數(shù)組的元素,有時(shí)也稱(chēng)為下標(biāo)變量。數(shù)組是在程序設(shè)計(jì)中,為了處理方便,?把具有相同類(lèi)型的若干元素按有序的形式組織起來(lái)的一種形式
    2022-03-03
  • Spring溫故而知新系列教程之AOP代理

    Spring溫故而知新系列教程之AOP代理

    Spring AOP 是代理模式的應(yīng)用,可以使用JDK提供的Proxy類(lèi)或通過(guò)字節(jié)碼增強(qiáng)來(lái)實(shí)現(xiàn)。下面這篇文章主要給大家介紹了關(guān)于Spring之AOP代理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-05-05
  • java中的數(shù)學(xué)計(jì)算函數(shù)的總結(jié)

    java中的數(shù)學(xué)計(jì)算函數(shù)的總結(jié)

    這篇文章主要介紹了java中的數(shù)學(xué)計(jì)算函數(shù)的總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Java讀取文件的幾種方式詳細(xì)總結(jié)

    Java讀取文件的幾種方式詳細(xì)總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java讀取文件的幾種方式,文中通過(guò)代碼示例將幾種方式介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-08-08
  • IDEA2020.3.2版本自動(dòng)注釋類(lèi)和方法注釋模板配置步驟詳解

    IDEA2020.3.2版本自動(dòng)注釋類(lèi)和方法注釋模板配置步驟詳解

    這篇文章主要介紹了IDEA2020.3.2版本自動(dòng)注釋類(lèi)和方法注釋模板配置步驟,本文給大家分享了我自己創(chuàng)建過(guò)程通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • MyBatis批量查詢(xún)、插入、更新、刪除的實(shí)現(xiàn)示例

    MyBatis批量查詢(xún)、插入、更新、刪除的實(shí)現(xiàn)示例

    由于需要處理短時(shí)間內(nèi)大量數(shù)據(jù)入庫(kù)的問(wèn)題,想到了Mybatis的批量操作,本文主要介紹了MyBatis批量查詢(xún)、插入、更新、刪除的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2023-05-05
  • 通過(guò)Java實(shí)現(xiàn)添加或刪除PDF中的附件

    通過(guò)Java實(shí)現(xiàn)添加或刪除PDF中的附件

    當(dāng)我們?cè)谥谱鱌DF文件或者PPT演示文稿的時(shí)候,為了讓自己的文件更全面詳細(xì),就會(huì)在文件中添加附件。本文為大家整理了Java實(shí)現(xiàn)添加或刪除PDF中的附件的方法,需要的可以參考下
    2023-01-01

最新評(píng)論