數據結構與算法學習筆記之先進先出的隊列


前言

  隊列是一種非常實用的數據結構,類似於生活中發排隊,可應用於生活,開發中各個方面,比如共享打印機(先請求先打印),消息隊列。你想知道他們是怎么工作的么。那就來一起學習一下隊列吧

 

正文

一、隊列的定義?

1.一種先進先出的線性表

2.只允許入棧 push()和出棧 pop()

在后端(稱為rear)進行插入操作,在前端(稱為front)進行刪除操作。

 

二、如何用代碼實現隊列?

1.java中JDK提供了Queue接口

使得LinkedList實現了該接口,所以使用隊列的時候,一般采用LinkedList。因為LinkedList是雙向鏈表,可以很方便的實現隊列的所有功能。

Queue使用時要盡量避免Collection的add()和remove()方法,而是要使用offer()來加入元素,使用poll()來獲取並移出元素。它們的優點是通過返回值可以判斷成功與否,add()和remove()方法在失敗的時候會拋出異常。 如果要使用前端而不移出該元素,使用
element()或者peek()方法。

 

2.數組實現(順序隊列)

 

public class ArrayQueue { //存儲數據的數組
private String[] items; //記錄數組容量
private int n; private int size; //head記錄隊頭索引,tail記錄隊尾索引
private int head = 0; private int tail = 0; //申請一個指定容量的隊列
public ArrayQueue(int capacity){ items = new String[capacity]; n = capacity; } /* * 入隊: * 1.堆滿的時,入隊失敗 * 1.1頻繁出入隊,造成數組使用不連續 * 1.2在入隊的時候,集中觸發進行數據搬移 * 2.在末尾插入數據,注意tail指向隊尾元素的索引+1 */
public boolean enqueue(String item){ //表示隊滿
if(head == 0 && tail == n) return false; //表示需要數據搬移
else if(head != 0 && tail == n){ for (int i = head; i < tail; i++) { items[i-head] = items[i]; } head = 0; tail = tail - head; } //將數據加入隊列
items[tail++] = item; size++; return true; } //出隊:1.隊空時,出隊失敗;2.出隊,head索引+1
public String dequeue(){ String res = null; if(head == tail) return res; res = items[head++]; size--; return res; } }

(代碼來源於姜威)

 

3.鏈表實現(鏈式隊列)

 

public class LinkedQueue { //定義一個節點類
private class Node{ String value; Node next; } //記錄隊列元素個數
private int size = 0; //head指向隊頭結點,tail指向隊尾節點
private Node head; private Node tail; //申請一個隊列
public LinkedQueue(){} //入隊
public boolean enqueue(String item){ Node newNode = new Node(); newNode.value = item; if (size == 0) head = newNode; else tail.next = newNode; tail = newNode; size++; return true; } //出隊
public String dequeue(){ String res = null; if(size == 0) return res; if(size == 1) tail = null; res = head.value; head = head.next; size--; return res; } }

(代碼來源於姜威)

4.循環隊列(基於數組)

循環隊列的實現 public class LoopArrayQueue { //存儲數據的數組
private String[] items; //記錄數組容量
private int n; private int size = 0; //head記錄隊頭索引,tail記錄隊尾索引
private int head = 0; private int tail = 0; //申請一個指定容量的隊列
public LoopArrayQueue(int capacity){ items = new String[capacity]; n = capacity; } //入隊:關鍵在於隊滿的條件
public boolean enqueue(String item){ if ((tail + 1) % n == head) return false; items[tail] = item; tail = (tail + 1) % n; size++; return true; } //出隊:關鍵在於隊空的條件
public String dequeue(){ String res = null; if(head == tail) return res; res = items[head]; head = (head + 1) % n; size--; return res; } }

(代碼來源於姜威)

 

三、隊列的常見的應用阻塞隊列和並發隊列

 

1.阻塞隊列


1)在隊列的基礎上增加阻塞操作,形成了阻塞隊列。
2)阻塞隊列就是在隊列為空的時候,從隊頭取數據會被阻塞,因為此時還沒有數據可取,直到隊列中有了數據才能返回;如果隊列已經滿了,那么插入數據的操作就會被阻塞,直到隊列中有空閑位置后再插入數據,然后在返回。
3)“生產者-消費者模型”

(圖片來源於王爭)

基於阻塞隊列實現的“生產者-消費者模型”可以有效地協調生產和消費的速度。

當“生產者”生產數據的速度過快,“消費者”來不及消費時,存儲數據的隊列很快就會滿了,這時生產者就阻塞等待,直到“消費者”消費了數據,“生產者”才會被喚醒繼續生產。不僅如此,基於阻塞隊列,我們還可以通過協調“生產者”和“消費者”的個數,來提高數據處理效率,比如配置幾個消費者,來應對一個生產者。

 

2.並發隊列

1)在多線程的情況下,會有多個線程同時操作隊列,這時就會存在線程安全問題。

  線程安全問題的隊列就稱為並發隊列。
2)並發隊列簡單的實現就是在enqueue()、dequeue()方法上加鎖,但是鎖粒度大並發度會比較低,同一時刻僅允許一個存或取操作。
3)基於數組的循環隊列利用CAS原子操作,可以實現非常高效的並發隊列。這也是循環隊列比鏈式隊列應用更加廣泛的原因。
3.線程池資源枯竭是的處理
在資源有限的場景,當沒有空閑資源時,基本上都可以通過“隊列”這種數據結構來實現請求排隊。

 

四、隊列在線程池等有限資源中的應用

當我們向固定大小的線程池中請求一個線程時,如果線程池中沒有空閑資源了,這個時候線程池如何處理這個請求?是拒絕請求還是排隊請求?各種處理策略又是怎么實現的呢?

兩種處理策略:

  非阻塞的處理方式,直接拒絕任務請求

  阻塞的處理方式,將請求排隊,等有空閑線程,取出隊列中請求繼續處理

基於鏈表的實現方式,可以實現一個支持無線排隊的無界隊列,但是可能會導致過多的請求排隊,請求處理的響應時間過長

基於數組的實現的有界隊列,隊列的大小有限,所以線程池中排隊的請求超過隊列大小時,接下來的請求就會被拒絕,這種方式對響應時間敏感的系統,更加合適;

隊列可以應用在任何有限的資源池中,當沒有空閑資源都可以通過“隊列”來實現請求排隊

 

五、思考


1.除了線程池這種池結構會用到隊列排隊請求,還有哪些類似線程池結構或者場景中會用到隊列的排隊請求呢?

 

在很多偏底層的系統、框架、中間件的開發中,起着關鍵性的作用。比如高性能隊列 Disruptor、Linux 環形緩存,都用到了循環並發隊列;Java concurrent 並發包利用 ArrayBlockingQueue 來實現公平鎖等。

分布式消息隊列,如 kafka 也是一種隊列

 

2.今天講到並發隊列,關於如何實現無鎖的並發隊列,網上有很多討論。對這個問題,你怎么看?

 

可以使用 cas + 數組的方式實現。考慮使用CAS實現無鎖隊列,則在入隊前,獲取tail位置,入隊時比較tail是否發生變化,如果否,則允許入隊,反之,本次入隊失敗。出隊則是獲取head位置,進行cas

 

 

相關文章

 

數據結構與算法學習筆記之寫鏈表代碼的正確姿勢(下)

 

數據結構與算法學習筆記之 提高讀取性能的鏈表(上)

 

數據結構與算法學習筆記之 從0編號的數組

 

數據結構與算法學習筆記之后進先出的“桶”

 

 

以上內容為個人的學習筆記,僅作為學習交流之用。

 

歡迎大家關注公眾號,不定時干貨,只做有價值的輸出

作者:Dawnzhang 
出處:https://www.cnblogs.com/clwydjgs/p/9809279.html

 

版權:本文版權歸作者
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM