中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

多線程(二十、阻塞隊列-PriorityBlockingQueue)

發布時間:2020-08-02 21:58:08 來源:網絡 閱讀:569 作者:shayang88 欄目:編程語言

PriorityBlockingQueue簡介

1、一種優先級隊列,元素并不是以FIFO的方式出/入隊,而是以按照權重大小的順序出隊;
2、PriorityBlockingQueue是真正的×××隊列(僅受內存大小限制),它不像ArrayBlockingQueue那樣構造時必須指定最大容量,也不像LinkedBlockingQueue默認最大容量為Integer.MAX_VALUE;
3、PriorityBlockingQueue是按照元素的權重進入排序,所以隊列中的元素必須是可以比較的,也就是說元素必須實現Comparable接口;
4、PriorityBlockingQueue×××隊列,所以插入元素永遠不會阻塞線程;
5、PriorityBlockingQueue底層是一種基于數組實現的堆結構。

PriorityBlockingQueue構造

構造方法

1、PriorityBlockingQueue一共有4個構造方法,我們重點看看第三個

/**
 * 指定初始容量和比較器的構造器.
 */
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];
}

1、PriorityBlockingQueue內部利用了ReentrantLock來保證并發訪問時的線程安全。
2、PriorityBlockingQueue如果不指定容量,默認容量為11。
3、PriorityBlockingQueue只有一個條件等待隊列——notEmpty,因為會自動擴容,所以插入元素并不會阻塞,僅當隊列為空時,才可能阻塞“出隊”線程。

2、成員變量

public class PriorityBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /**
     * 默認容量,大小11
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    /**
     * 最大容量,并不是真正的×××
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 內部堆數組, 保存實際數據, 可以看成一顆二叉樹:
     * 對于頂點queue[n], queue[2*n+1]表示左子結點, queue[2*(n+1)]表示右子結點.
     */
    private transient Object[] queue;

    /**
     * 隊列中的元素個數.
     */
    private transient int size;

    /**
     * 比較器, 如果為null, 表示以元素自身的自然順序進行比較(元素必須實現Comparable接口).
     */
    private transient Comparator<? super E> comparator;

    /**
     * 全局鎖.
     */
    private final ReentrantLock lock;

    /**
     * 當隊列為空時,出隊線程在該條件隊列上等待.
     */
    private final Condition notEmpty;

    // ...
}

3、插入元素

1、put方法調用offer方法
2、offer方法

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();

    final ReentrantLock lock = this.lock;   // 加鎖
    lock.lock();

    int n, cap;
    Object[] array;
    while ((n = size) >= (cap = (array = queue).length))    // 隊列已滿, 則進行擴容
        tryGrow(array, cap);//擴容方法

    try {
        Comparator<? super E> cmp = comparator;
        if (cmp == null)    // 比較器為空, 則按照元素的自然順序進行堆調整
            siftUpComparable(n, e, array);//堆的上浮
        else                // 比較器非空, 則按照比較器進行堆調整
            siftUpUsingComparator(n, e, array, cmp);//堆的上浮
        size = n + 1;       // 隊列元素總數+1
        notEmpty.signal();  // 喚醒一個可能正在等待的"出隊線程"
    } finally {
        lock.unlock();
    }
    return true;
}

4、堆上浮

小頂堆,“上浮調整”,可以把堆可以想象成一棵完全二叉樹,每次插入元素都鏈接到二叉樹的最右下方,然后將插入的元素與其父結點比較,如果父結點大,則交換元素,直到沒有父結點比插入的結點大為止,大頂堆反之。

/**
 * 將元素x插入到array[k]的位置.
 * 然后按照元素的自然順序進行堆調整——"上浮",以維持"堆"有序.
 * 最終的結果是一個"小頂堆".
 */
private static <T> void siftUpComparable(int k, T x, Object[] array) {
    Comparable<? super T> key = (Comparable<? super T>) x;
    while (k > 0) {
        int parent = (k - 1) >>> 1;     // 相當于(k-1)除2, 就是求k結點的父結點所在數組的索引位置
        Object e = array[parent];
        if (key.compareTo((T) e) >= 0)  // 如果插入的結點值大于父結點, 則退出
            break;

        // 否則,交換父結點和當前結點的值
        array[k] = e;
        k = parent;
    }
    array[k] = key;
}

5、刪除元素

/**
 * 出隊一個元素.
 * 如果隊列為空, 則阻塞線程.
 */
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();   // 獲取全局鎖
    E result;
    try {
        while ((result = dequeue()) == null)    // 隊列為空
            notEmpty.await();                   // 線程在noEmpty條件隊列等待
    } finally {
        lock.unlock();
    }
    return result;
}
//堆出隊,每次都是出隊對頂元素
//對于“小頂堆”就是隊列中的最小值,對于“大頂堆”就是隊列中的最大值
private E dequeue() {
    int n = size - 1;   // n表示出隊后的剩余元素個數
    if (n < 0)          // 隊列為空, 則返回null
        return null;
    else {
        Object[] array = queue;
        E result = (E) array[0];    // array[0]是堆頂結點, 每次出隊都刪除堆頂結點
        E x = (E) array[n];         // array[n]是堆的最后一個結點, 也就是二叉樹的最右下結點
        array[n] = null;
        Comparator<? super E> cmp = comparator;
        if (cmp == null)
            siftDownComparable(0, x, array, n);//堆下沉
        else
            siftDownUsingComparator(0, x, array, n, cmp);//堆下沉
        size = n;
        return result;
    }
}

6、堆下沉

/**
 * 堆的"下沉"調整.
 * 刪除array[k]對應的結點,并重新調整堆使其有序.
 *
 * @param k     待刪除的位置
 * @param x     待比較的健
 * @param array 堆數組
 * @param 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;
        int half = n >>> 1;           // 相當于n除2, 即找到索引n對應結點的父結點
        while (k < half) {
            /**
             * 下述代碼中:
             * c保存k的左右子結點中的較小結點值 
             * child保存較小結點對應的索引
             */
            int child = (k << 1) + 1; // k的左子結點
            Object c = array[child];

            int right = child + 1;    // k的右子結點
            if (right < n && ((Comparable<? super T>) c).compareTo((T) array[right]) > 0)
                c = array[child = right];

            if (key.compareTo((T) c) <= 0)
                break;
            array[k] = c;
            k = child;
        }
        array[k] = key;
    }
}

下沉步驟:
1、頂點與最后一個節點交換,刪除最后一個節點即刪除頂點
2、最后一個節點位于頂點,開始下沉,找到左右子結點中較小的那個;
3、結點交換;
4、重復2-3步直到當前結點沒有左右子結點或比左右子結點都小。

總結

PriorityBlockingQueue屬于比較特殊的阻塞隊列,適用于有元素優先級要求的場景。它的內部和ArrayBlockingQueue一樣,使用一個了全局獨占鎖來控制同時只有一個線程可以進行入隊和出隊,近似×××隊列,入隊線程并不會阻塞。

PriorityBlockingQueue始終保證出隊的元素是優先級最高的元素,并且可以定制優先級的規則,內部通過使用堆(基于數組形式)來維護元素順序,它的內部數組是可擴容的,擴容和出/入隊可以并發進行。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

遂溪县| 鲁甸县| 潞城市| 宜阳县| 砚山县| 民县| 江源县| 慈利县| 伊春市| 吉隆县| 宣汉县| 邯郸县| 广安市| 承德县| 桐柏县| 亚东县| 安平县| 偃师市| 大新县| 嘉义县| 汾阳市| 涞水县| 龙井市| 湄潭县| 浦北县| 尤溪县| 呼和浩特市| 乌拉特中旗| 卢湾区| 大连市| 阜城县| 广南县| 来凤县| 洛扎县| 彰化县| 修武县| 施秉县| 周宁县| 隆子县| 皮山县| 青冈县|