Java 高频面试题:聊一聊 JUC 下的 LinkedBlockingQueue
??點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達 今日推薦:推薦 8 個常用 Spring Boot 項目個人原創(chuàng)+1博客:點擊前往,查看更多本文聊一下 JUC 下的 LinkedBlockingQueue 隊列,先說說 LinkedBlockingQueue 隊列的特點,然后再從源碼的角度聊一聊 LinkedBlockingQueue 的主要實現(xiàn)~
LinkedBlockingQueue 有以下特點:
「LinkedBlockingQueue 是阻塞隊列,底層是單鏈表實現(xiàn)的」~
「元素從隊列尾進隊,從隊列頭出隊,符合FIFO」~
「可以使用 Collection 和 Iterator 兩個接口的所有操作,因為實現(xiàn)了兩者的接口」~
「LinkedBlockingQueue 隊列讀寫操作都加了鎖,但是讀寫用的是兩把不同的鎖,所以可以同時讀寫操作」~
LinkedBlockingQueue 隊列繼承了 AbstractQueue 類,實現(xiàn)了 BlockingQueue 接口,LinkedBlockingQueue ?主要有以下接口:
//將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量) //在成功時返回 true,如果此隊列已滿,則拋IllegalStateException。? boolean?add(E?e);?//將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量)? //?將指定的元素插入此隊列的尾部,如果該隊列已滿,? //則在到達指定的等待時間之前等待可用的空間,該方法可中斷? boolean?offer(E?e,?long?timeout,?TimeUnit?unit)?throws?InterruptedException;?//將指定的元素插入此隊列的尾部,如果該隊列已滿,則一直等到(阻塞)。? void?put(E?e)?throws?InterruptedException;?//獲取并移除此隊列的頭部,如果沒有元素則等待(阻塞),? //直到有元素將喚醒等待線程執(zhí)行該操作? E?take()?throws?InterruptedException;?//獲取并移除此隊列的頭,如果此隊列為空,則返回 null。? E?poll(); //獲取并移除此隊列的頭部,在指定的等待時間前一直等到獲取元素,?//超過時間方法將結(jié)束 E?poll(long?timeout,?TimeUnit?unit)?throws?InterruptedException;?//從此隊列中移除指定元素的單個實例(如果存在)。? boolean?remove(Object?o);?//獲取但不移除此隊列的頭元素,沒有則跑異常NoSuchElementException? E?element();?//獲取但不移除此隊列的頭;如果此隊列為空,則返回 null。? E?peek();?LinkedBlockingQueue 隊列的讀寫方法非常的多,但是常用的是 put()、take()方法,因為它們兩是阻塞的,所以我們就從源碼的角度來聊一聊 LinkedBlockingQueue 隊列中這兩個方法的實現(xiàn)。
先來看看 put()方法,源碼如下:
public?void?put(E?e)?throws?InterruptedException?{if?(e?==?null)?throw?new?NullPointerException();//?預先設(shè)置?c?的值為?-1,表示失敗int?c?=?-1;Node<E>?node?=?new?Node<E>(e);//?獲取寫鎖final?ReentrantLock?putLock?=?this.putLock;//?獲取當前隊列的大小final?AtomicInteger?count?=?this.count;//?設(shè)置可中斷鎖putLock.lockInterruptibly();try?{//?隊列滿了//?當前線程阻塞,等待其他線程的喚醒(其他線程?take?成功后就會喚醒此處線程)while?(count.get()?==?capacity)?{//?無限期等待notFull.await();}//?新增到隊列尾部enqueue(node);//?獲取當前的隊列數(shù)c?=?count.getAndIncrement();//?如果隊列未滿,嘗試喚醒一個put的等待線程if?(c?+?1?<?capacity)notFull.signal();}?finally?{//?釋放鎖putLock.unlock();}if?(c?==?0)signalNotEmpty(); }put()方法的源碼并不難,非常容易就看懂,put()方法的過程大概如下:
1、先加鎖,保證容器的并發(fā)安全~
2、隊列新增數(shù)據(jù),將數(shù)據(jù)追加到隊列尾部~
3、新增時,如果隊列滿了,當前線程是會被阻塞的,等待被喚醒~
4、新增數(shù)據(jù)成功后,在適當時機,會喚起 put 的等待線程(隊列不滿時),或者 take 的等待線程(隊列不為空時),這樣保證隊列一旦滿足 put 或者 take 條件時,立馬就能喚起阻塞線程,繼續(xù)運行,保證了喚起的時機不被浪費offer 就有兩兩種,一種是直接返回 false,另一種是超過一定時間后返回 false~
5、釋放鎖~
其他的新增方法,例如 offer,可以查看源碼,跟put() 方法大同小異,相差不大~
再來看看 take()方法,源碼如下:
public?E?take()?throws?InterruptedException?{E?x;//?默認負數(shù)int?c?=?-1;//?當前鏈表的個數(shù)final?AtomicInteger?count?=?this.count;//獲取讀鎖final?ReentrantLock?takeLock?=?this.takeLock;takeLock.lockInterruptibly();try?{//?當隊列為空時,阻塞,等待其他線程喚醒?while?(count.get()?==?0)?{notEmpty.await();}//?從隊列的頭部拿出一個元素x?=?dequeue();//減一操作,C比真實的隊列數(shù)據(jù)大一c?=?count.getAndDecrement();//?c?大于?0?,表示隊列有值,可以喚醒之前被阻塞的讀線程if?(c?>?1)notEmpty.signal();}?finally?{//?釋放鎖takeLock.unlock();}//?隊列未滿,可以喚醒?put?等待線程~if?(c?==?capacity)signalNotFull();return?x; }take()方法跟 put() 方法類似,是一個相反的操作,我就不做過多的說明了~
以上就是 LinkedBlockingQueue 隊列的簡單源碼解析,希望對你的面試或者工作有所幫助,感謝你的閱讀~
最后,再附上我歷時三個月總結(jié)的?Java 面試 + Java 后端技術(shù)學習指南,這是本人這幾年及春招的總結(jié),目前,已經(jīng)拿到了大廠offer,拿去不謝!
下載方式
1.?首先掃描下方二維碼
2.?后臺回復「Java面試」即可獲取
總結(jié)
以上是生活随笔為你收集整理的Java 高频面试题:聊一聊 JUC 下的 LinkedBlockingQueue的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从偏远的小山村出来的孩子,一路的 “辛酸
- 下一篇: Redis 到底是怎么实现“附近的人”这