日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JUC队列-LinkedBlockingQueue(二)

發布時間:2024/2/28 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JUC队列-LinkedBlockingQueue(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

LinkedBlockingQueue介紹

LinkedBlockingQueue是一個以單向鏈表實現的阻塞隊列,鏈表隊列的吞吐量通常高于基于數組的隊列,但是在大多數場景中,其可預見的性能要低。

要注意的是,LinkedBlockingQueue可選容量,防止膨脹,默認大小為Integer.MAX_VALUE。

LinkedBlockingQueue的UML圖:

  • LinkedBlockingQueue繼承于AbstractQueue,它本質上是一個FIFO(先進先出)的隊列。
  • LinkedBlockingQueue實現了BlockingQueue接口,它支持多線程并發。當多線程競爭同一個資源時,某線程獲取到該資源之后,其它線程需要阻塞等待。
  • LinkedBlockingQueue是通過單鏈表實現的。
    • head是鏈表的表頭。取出數據時,都是從表頭head處插入。
    • last是鏈表的表尾。新增數據時,都是從表尾last處插入。
    • count是鏈表的實際大小,即當前鏈表中包含的節點個數。
    • capacity是列表的容量,它是在創建鏈表時指定的。
    • putLock是插入鎖,takeLock是取出鎖;notEmpty是“非空條件”,notFull是“未滿條件”。通過它們對鏈表進行并發控制。
  • LinkedBlockingQueue在實現“多線程對競爭資源的互斥訪問”時,對于“插入”和“取出(刪除)”操作分別使用了不同的鎖。對于插入操作,通過“插入鎖putLock”進行同步;對于取出操作,通過“取出鎖takeLock”進行同步。

    此外,插入鎖putLock和“非滿條件notFull”相關聯,取出鎖takeLock和“非空條件notEmpty”相關聯。通過notFull和notEmpty更細膩的控制鎖。

    LinkedBlockingQueued的源碼分析

    構造方法

    public LinkedBlockingQueue(int capacity) {if (capacity <= 0) throw new IllegalArgumentException();this.capacity = capacity;last = head = new Node<E>(null); }

    其他靜態初始化:

    // 容量 private final int capacity; // 當前數量 private final AtomicInteger count = new AtomicInteger(0); private transient Node<E> head; // 鏈表的表頭 private transient Node<E> last; // 鏈表的表尾 // 用于控制“刪除元素”的互斥鎖takeLock 和 鎖對應的“非空條件”notEmpty private final ReentrantLock takeLock = new ReentrantLock(); private final Condition notEmpty = takeLock.newCondition(); // 用于控制“添加元素”的互斥鎖putLock 和 鎖對應的“非滿條件”notFull private final ReentrantLock putLock = new ReentrantLock(); private final Condition notFull = putLock.newCondition();

    添加元素

    public void put(E e) throws InterruptedException {if (e == null) throw new NullPointerException();int c = -1;Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;//獲取插入鎖putLock.lockInterruptibly();try {//如果隊列已經滿了則awaitwhile (count.get() == capacity) {notFull.await();}//不滿則插入結點enqueue(node);c = count.getAndIncrement();// 如果在插入元素之后,隊列仍然未滿,則喚醒notFull上的等待線程。if (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}// 如果在插入節點前,隊列為空;則插入節點后,喚醒notEmpty上的等待線程if (c == 0)signalNotEmpty();}

    enqueue()方法很簡單:

    private void enqueue(Node<E> node) {// assert putLock.isHeldByCurrentThread();// assert last.next == null;last = last.next = node; }

    取出元素

    public E take() throws InterruptedException {E x;int c = -1;final AtomicInteger count = this.count;final ReentrantLock takeLock = this.takeLock;// 獲取“取出鎖”,若當前線程是中斷狀態,則拋出InterruptedException異常takeLock.lockInterruptibly();try {// 若“隊列為空”,則一直等待。while (count.get() == 0) {notEmpty.await();}// 取出元素x = dequeue();// 取出元素之后,將“節點數量”-1;并返回“原始的節點數量”。c = count.getAndDecrement();if (c > 1)notEmpty.signal();} finally {// 釋放“取出鎖”takeLock.unlock();}// 如果在“取出元素之前”,隊列是滿的;則在取出元素之后,喚醒notFull上的等待線程。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 GChead = first;E x = first.item;first.item = null;return x; }

    總結:
    ArrayBlockingQueue和LinkedBlockingQueue有很多相似的地方,他們的不同點在于:

    他們都是阻塞隊列,但ArrayBlockingQueue是數組實現的,并且是有界限的;而LinkedBlockingQueue是鏈表實現的,是無界限的,而且ArrayBlockingQueue只用了一個獨占鎖,而LinkedBlockingQueue用了兩個獨占鎖,所以ArrayBlockingQueue沒有實現讀寫分離,而LinkedBlockingQueue可以。

    為什么要這么設計呢?

    LinkedBlockingQueue的較大一部分時間需要構造節點,導致較長的等待,所以同時存取有較大優化,而ArrayBlockingQueue的不用構造節點,加鎖和解鎖的時間可能占比較大。

    總結

    以上是生活随笔為你收集整理的JUC队列-LinkedBlockingQueue(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。