面試官:你好,你先做個自我介紹吧
某人:面試官你好,我叫開局一張嘴面試全靠吹,某某年畢業,畢業自家里蹲大學,做過某某專案,,,,,,
面試官微微一笑,捋了捋稀疏的頭發:看你簡歷,你精通多執行緒?那你手寫過堵塞佇列嗎?
某人心里出現一萬個問號,堵塞佇列是啥玩意?平時基本都是crud,頂多用多執行緒跑資料

某人:沒有手寫過,
面試官:哦,那你說下堵塞佇列吧
某人支支吾吾:這個有點忘了
面試官:沒事,那我們下一個,
此處省略一萬字,
面試官扭了扭嚴重負荷的頸椎:先到這里吧,你先回去等通知,
某人:好的,
不出意外,某人等了一個月,等的望眼欲穿,也沒等到那個期待的電話,
1.什么是佇列
佇列是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進行洗掉操作,而在表的后端(rear)進行插入操作,和堆疊一樣,佇列是一種操作受限制的線性表,進行插入操作的端稱為隊尾,進行洗掉操作的端稱為隊頭,

佇列其實就是跟平時排隊一樣,按照順序來,先排隊的先買到東西,后排隊的后買到東西,排隊的第一個叫隊頭,最后一個叫隊尾,這就是佇列的先進先出,這是和堆疊最大的區別,
2.什么是堵塞佇列?

當佇列為空時,消費者掛起,佇列已滿時,生產者掛起,這就是生產-消費者模型,堵塞其實就是將執行緒掛起,
因為生產者的生產速度和消費者的消費速度之間的不匹配,就可以通過堵塞佇列讓速度快的暫時堵塞,
如生產者每秒生產兩個資料,而消費者每秒消費一個資料,當佇列已滿時,生產者就會堵塞(掛起),等待消費者消費后,再進行喚醒,
堵塞佇列會通過掛起的方式來實作生產者和消費者之間的平衡,這是和普通佇列最大的區別,
3.如何實作堵塞佇列?
jdk其實已經幫我們提供了實作方案,java5增加了concurrent包,concurrent包中的BlockingQueue就是堵塞佇列,我們不需要關心BlockingQueue如何實作堵塞,一切都幫我們封裝好了,只需要做一個沒有感情的API呼叫者就行,
4.BlockingQueue如何使用?
BlockingQueue本身只是一個介面,規定了堵塞佇列的方法,主要依靠幾個實作類實作,
4.1BlockingQueue主要方法

1.插入資料
(1)offer(E e):如果佇列沒滿,回傳true,如果佇列已滿,回傳false(不堵塞)
(2)offer(E e, long timeout, TimeUnit unit):可以設定等待時間,如果佇列已滿,則進行等待,超過等待時間,則回傳false
(3)put(E e):無回傳值,一直等待,直至佇列空出位置
2.獲取資料
(1)poll():如果有資料,出隊,如果沒有資料,回傳null
(2)poll(long timeout, TimeUnit unit):可以設定等待時間,如果沒有資料,則等待,超過等待時間,則回傳null
(3)take():如果有資料,出隊,如果沒有資料,一直等待(堵塞)
4.2BlockingQueue主要實作類
1.ArrayBlockingQueue:ArrayBlockingQueue是基于陣列實作的,通過初始化時設定陣列長度,是一個有界佇列,而且ArrayBlockingQueue和LinkedBlockingQueue不同的是,ArrayBlockingQueue只有一個鎖物件,而LinkedBlockingQueue是兩個鎖物件,一個鎖物件會造成要么是生產者獲得鎖,要么是消費者獲得鎖,兩者競爭鎖,無法并行,
2.LinkedBlockingQueue:LinkedBlockingQueue是基于鏈表實作的,和ArrayBlockingQueue不同的是,大小可以初始化設定,如果不設定,默認設定大小為Integer.MAX_VALUE,LinkedBlockingQueue有兩個鎖物件,可以并行處理,
3.DelayQueue:DelayQueue是基于優先級的一個無界佇列,佇列元素必須實作Delayed介面,支持延遲獲取,元素按照時間排序,只有元素到期后,消費者才能從佇列中取出,
4.PriorityBlockingQueue:PriorityBlockingQueue是基于優先級的一個無界佇列,底層是基于陣列存盤元素的,元素按照優選級順序存盤,優先級是通過Comparable的compareTo方法來實作的(自然排序),和其他堵塞佇列不同的是,其只會堵塞消費者,不會堵塞生產者,陣列會不斷擴容,這就是一個彩蛋,使用時要謹慎,
5.SynchronousQueue:SynchronousQueue是一個特殊的佇列,其內部是沒有容器的,所以生產者生產一個資料,就堵塞了,必須等消費者消費后,生產者才能再次生產,稱其為佇列有點不合適,現實生活中,多個人才能稱為隊,一個人稱為隊有些說不過去,
5.手寫堵塞佇列
我是參照了ArrayBlockingQueue的原始碼寫的,歡迎大家斧正,
/**
* @author yz
* @version 1.0
* @date 2020/10/31 11:24
*/
public class YzBlockingQuery {
private Object[] tab; //佇列容器
private int takeIndex; //出隊下標
private int putIndex; //入隊下標
private int size;//元素數量
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition notEmpty;//讀條件
private Condition notFull;//寫條件
public YzBlockingQuery(int tabCount) {
if (tabCount <= 0) {
new NullPointerException();
}
tab = new Object[tabCount];
notEmpty = reentrantLock.newCondition();
notFull = reentrantLock.newCondition();
}
public boolean offer(Object obj) {
if (obj == null) { throw new NullPointerException(); }
try {
//獲取鎖
reentrantLock.lock();
//佇列已滿
while (size==tab.length){
System.out.println("佇列已滿");
//堵塞
notFull.await();
}
tab[putIndex]=obj;
if(++putIndex==tab.length){
putIndex=0;
}
size++;
//喚醒讀執行緒
notEmpty.signal();
return true;
} catch (Exception e) {
//喚醒讀執行緒
notEmpty.signal();
} finally {
reentrantLock.unlock();
}
return false;
}
public Object take(){
try {
reentrantLock.lock();
while (size==0){
System.out.println("佇列空了");
//堵塞
notEmpty.await();
}
Object obj= tab[takeIndex];
//如果到了最后一個,則從頭開始
if(++takeIndex==tab.length){
takeIndex=0;
}
size--;
//喚醒寫執行緒
notFull.signal();
return obj;
}catch (Exception e){
//喚醒寫執行緒
notFull.signal();
}finally {
reentrantLock.unlock();
}
return null;
}
public static void main(String[] args) {
Random random = new Random(100);
YzBlockingQuery yzBlockingQuery=new YzBlockingQuery(5);
Thread thread1 = new Thread(() -> {
for (int i=0;i<100;i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
yzBlockingQuery.offer(i);
System.out.println("生產者生產了:"+i);
}
});
Thread thread2 = new Thread(() -> {
for (int i=0;i<100;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object take = yzBlockingQuery.take();
System.out.println("消費者消費了:"+take);
}
});
thread1.start();
thread2.start();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/205276.html
標籤:其他
上一篇:2020-11-02
下一篇:實驗一 網路掃描與網路偵察
