chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

關于PriorityBlockingQueue中隊列操作

openEuler ? 來源:openEuler ? 作者:openEuler ? 2022-05-07 16:43 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

編者按:筆者在使用PriorityBlockingQueue實現按照優(yōu)先級處理任務時遇到一類NPE問題,經過分析發(fā)現根本原因是在任務出隊列時調用比較器異常,進而導致后續(xù)任務出隊列拋出NullPointerException。本文通過完整的案例復現來演示在什么情況會觸發(fā)該問題,同時給出了處理建議。希望讀者在編程時加以借鑒,避免再次遇到此類問題。

背景知識

PriorityBlockingQueue是一個無界的基于數組的優(yōu)先級阻塞隊列,使用一個全局ReentrantLock來控制某一時刻只有一個線程可以進行元素出隊和入隊操作,并且每次出隊都返回優(yōu)先級別最高的或者最低的元素。PriorityBlockingQueue通過以下兩種方式實現元素優(yōu)先級排序:

  1. 入隊元素實現Comparable接口來比較元素優(yōu)先級;
  2. PriorityBlockingQueue構造函數指定Comparator來比較元素優(yōu)先級;

關于PriorityBlockingQueue中隊列操作的部分,基本和PriorityQueue邏輯一致,只不過在操作時加鎖了。在本文中我們主要關注PriorityBlockingQueue出隊的take方法,該方法通過調用dequeue方法將元素出隊列。當沒有元素可以出隊的時候,線程就會阻塞等待。

publicEtake()throwsInterruptedException{
finalReentrantLocklock=this.lock;
lock.lockInterruptibly();
Eresult;
try{
//嘗試獲取最小元素,即小頂堆第一個元素,然后重新排序,如果不存在表示隊列暫無元素,進行阻塞等待。
while((result=dequeue())==null)
notEmpty.await();
}finally{
lock.unlock();
}
returnresult;
}

現象

在某個業(yè)務服務中使用PriorityBlockingQueue實現按照優(yōu)先級處理任務,某一天環(huán)境中的服務突然間不處理任務了,查看后臺日志,發(fā)現一直拋出NullPointerException。將進程堆dump出來,使用MAT發(fā)現某個PriorityBlockingQueue中的size值比實際元素個數多1個(入隊時已經對任務進行非空校驗)。

異常堆棧如下:

java.lang.NullPointerException
atjava.util.concurrent.PriorityBlockingQueue.siftDownComparable(PriorityBlockingQueue.java:404)
atjava.util.concurrent.PriorityBlockingQueue.dequeue(PriorityBlockingQueue.java:333)
atjava.util.concurrent.PriorityBlockingQueue.take(PriorityBlockingQueue.java:548)
...

MAT結果:

4f7ab378-cd26-11ec-bce3-dac502259ad0.png

原因分析

在此我們分析下PriorityBlockingQueue是如何出隊列的,PriorityBlockingQueue最終通過調用dequeue方法出隊列,dequeue方法處理邏輯如下:

  1. 將根節(jié)點(array[0])賦值給result;
  2. array[n] 賦值給 arrary[0];
  3. 將 array[n] 設置為 null;
  4. 調用siftDownComparable或siftDownUsingComparator對隊列元素重新排序;
  5. size大小減1;
  6. 返回result;

如果在第4步中出現異常,就會出現隊列中的元素個數比實際的元素個數多1個的現象。此時size未發(fā)生改變,arry[n]已經被置為null,再進行siftDown操作時就會拋出NullPointerException。繼續(xù)分析第4步中在什么情況下會出現異常,通過代碼走讀我們可以發(fā)現只有在調用Comparable#compareTo或者Comparator#compare方法進行元素比較的時候才可能出現異常。這塊代碼的處理邏輯和業(yè)務相關,如果業(yè)務代碼處理不當拋出異常,就會導致上述現象。

/**
*Mechanicsforpoll().Callonlywhileholdinglock.
*/
privateEdequeue(){
intn=size-1;
if(n0)
returnnull;
else{
Object[]array=queue;
Eresult=(E)array[0];//step1
Ex=(E)array[n];//step2
array[n]=null;//step3
ComparatorsuperE>cmp=comparator;
if(cmp==null)//step4 如果指定了comparator,就按照指定的comparator來比較。否則就按照默認的
siftDownComparable(0,x,array,n);
else
siftDownUsingComparator(0,x,array,n,cmp);
size=n;//step5
returnresult;//step6
}
}

privatestaticvoidsiftDownComparable(intk,Tx,Object[]array,intn){
if(n>0){
ComparablesuperT>key=(ComparablesuperT>)x;
inthalf=n>>>1;
while(kintchild=(k<1)+1;
Objectc=array[child];
intright=child+1;
if(rightsuperT>)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;
}
}
privatestaticvoidsiftDownUsingComparator(intk,Tx,Object[]array,intn,
ComparatorsuperT>cmp){
if(n>0){
inthalf=n>>>1;
while(kintchild=(k<1)+1;
Objectc=array[child];
intright=child+1;
if(right0)
c=array[child=right];
if(cmp.compare(x,(T)c)<=?0)
break;
array[k]=c;
k=child;
}
array[k]=x;
}
}

復現代碼

importjava.util.ArrayList;
importjava.util.List;
importjava.util.concurrent.PriorityBlockingQueue;

publicclassPriorityBlockingQueueTest{
staticclassEntityimplementsComparable<Entity>{
privateintid;
privateStringname;
privatebooleanflag;

publicvoidsetFlag(booleanflag){
this.flag=flag;
}

publicEntity(intid,Stringname){
this.id=id;
this.name=name;
}

@Override
publicintcompareTo(Entityentity){
if(flag){
thrownewRuntimeException("TestException");
}
if(entity==null||this.id>entity.id){
return1;
}
returnthis.id==entity.id?0:-1;
}
}

publicstaticvoidmain(String[]args){
intnum=5;
PriorityBlockingQueuepriorityBlockingQueue=newPriorityBlockingQueue<>();
Listentities=newArrayList<>();
for(inti=0;inewEntity(i,"entity"+i);
entities.add(entity);
priorityBlockingQueue.offer(entity);
}

entities.get(num-1).setFlag(true);
intsize=entities.size();
for(inti=0;itry{
priorityBlockingQueue.take();
}catch(Exceptione){
e.printStackTrace();
}
}
}

執(zhí)行結果如下:

java.lang.RuntimeException:TestException
atPriorityBlockingQueueTest$Entity.compareTo(PriorityBlockingQueueTest.java:31)
atPriorityBlockingQueueTest$Entity.compareTo(PriorityBlockingQueueTest.java:8)
atjava.util.concurrent.PriorityBlockingQueue.siftDownComparable(PriorityBlockingQueue.java:404)
atjava.util.concurrent.PriorityBlockingQueue.dequeue(PriorityBlockingQueue.java:333)
atjava.util.concurrent.PriorityBlockingQueue.take(PriorityBlockingQueue.java:548)
atPriorityBlockingQueueTest.main(PriorityBlockingQueueTest.java:71)
java.lang.NullPointerException
atjava.util.concurrent.PriorityBlockingQueue.siftDownComparable(PriorityBlockingQueue.java:404)
atjava.util.concurrent.PriorityBlockingQueue.dequeue(PriorityBlockingQueue.java:333)
atjava.util.concurrent.PriorityBlockingQueue.take(PriorityBlockingQueue.java:548)
atPriorityBlockingQueueTest.main(PriorityBlockingQueueTest.java:71)

規(guī)避方案

可以通過以下兩種方法規(guī)避:

  • 在take方法出現NPE時,清除隊列元素,將未處理的元素重新進入隊列;
  • 在 Comparable#compareTo 或 Comparator#compare 方法中做好異常處理,對異常情況進行默認操作;

建議使用后者。

案例引申

使用PriorityBlockingQueue作為緩存隊列來創(chuàng)建線程池時,使用submit提交任務會出現 java.lang.ClassCastException: java.util.concurrent.FutureTask cannot be cast to 異常,而使用execute沒有問題。

觀察submit源碼可以發(fā)現在submit內部代碼會將Runable封裝成RunnableFuture對象,然后調用execute提交任務。

publicFuturesubmit(Runnabletask){
if(task==null)thrownewNullPointerException();
RunnableFutureftask=newTaskFor(task,null);
execute(ftask);
returnftask;
}

以Comparable為例,任務入隊列時,最終會調用siftUpComparable方法。該方法第一步將RunnableFuture強轉為Comparable類型,而RunnableFuture類未實現Comparable接口,進而拋出ClassCastException異常。

publicbooleanoffer(Ee){
if(e==null)
thrownewNullPointerException();
finalReentrantLocklock=this.lock;
lock.lock();
intn,cap;
Object[]array;
while((n=size)>=(cap=(array=queue).length))
tryGrow(array,cap);
try{
ComparatorsuperE>cmp=comparator;
if(cmp==null)
siftUpComparable(n,e,array);
else
siftUpUsingComparator(n,e,array,cmp);
size=n+1;
notEmpty.signal();
}finally{
lock.unlock();
}
returntrue;
}

privatestaticvoidsiftUpComparable(intk,Tx,Object[]array){
ComparablesuperT>key=(ComparablesuperT>)x;
while(k>0){
intparent=(k-1)>>>1;
Objecte=array[parent];
if(key.compareTo((T)e)>=0)
break;
array[k]=e;
k=parent;
}
array[k]=key;
}

這也是常見的比較器調用異常案例,本文不再贅述,可自行參考其他文章。

總結

在使用PriorityBlockingQueue時,注意在比較器中做好異常處理,避免出現類似問題。

審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • 比較器
    +關注

    關注

    14

    文章

    1871

    瀏覽量

    110868
  • 數組
    +關注

    關注

    1

    文章

    420

    瀏覽量

    27017

原文標題:畢昇 JDK | PriorityBlockingQueue比較器異常導致的NPE問題分析

文章出處:【微信號:openEulercommunity,微信公眾號:openEuler】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    NVMe高速傳輸之擺脫XDMA設計38:隊列管理功能驗證與分析2

    波形如圖1 所示。 創(chuàng)建深度為 1024 的 I/O 提交隊列, 由于支持的最大隊列深度為 1023, 所以創(chuàng)建返回狀態(tài) cr_status 值為 4, 表示創(chuàng)建深度錯誤, 仿真行為符合設計預期
    發(fā)表于 10-15 08:14

    NVMe高速傳輸之擺脫XDMA設計37:隊列管理功能驗證與分析1

    隊列、 刪除隊列、 隊列邊界、 操作順序等功能的測試,待測設計的設計目標中, 提交隊列的最大數量為 16, 單
    發(fā)表于 10-13 11:17

    NVMe IP高速傳輸卻不依賴XDMA設計之九:隊列管理模塊(上)

    這是采用PCIe設計NVMe,并非調用XDMA方式,后者在PCIe4.0時不大方便,故團隊直接采用PCIe設計,結合UVM驗證加快設計速度。 隊列管理模塊采用隊列的存儲與控制分離的設計結構。
    的頭像 發(fā)表于 08-04 09:53 ?506次閱讀
    NVMe IP高速傳輸卻不依賴XDMA設計之九:<b class='flag-5'>隊列</b>管理模塊(上)

    NVMe高速傳輸之擺脫XDMA設計十:隊列管理模塊設計(下)

    ?續(xù)上,隊列管理模塊采用隊列的存儲與控制分離的設計結構,如圖1所示為隊列管理模塊的結構框圖。 ?編輯 圖1 隊列管理結構 由于提交隊列管理單
    發(fā)表于 07-30 16:27

    NVMe高速傳輸之擺脫XDMA設計九:隊列管理模塊設計(上)

    設計,結合UVM驗證加快設計速度。隊列管理模塊采用隊列的存儲與控制分離的設計結構,如圖1所示為隊列管理模塊的結構框圖。 圖1隊列管理模塊結構圖對于提交
    發(fā)表于 07-27 17:41

    RabbitMQ消息隊列解決方案

    在現代分布式系統架構中,消息隊列作為核心組件,承擔著系統解耦、異步處理、流量削峰等重要職責。RabbitMQ作為一款成熟的消息隊列中間件,以其高可用性、高可靠性和豐富的特性,成為眾多企業(yè)的首選方案。本文將從運維工程師的角度,詳細闡述RabbitMQ從單機部署到集群搭建的完
    的頭像 發(fā)表于 07-08 15:55 ?350次閱讀

    從 app_gatt_callback調用這個隊列推送函數時,程序出現了硬故障怎么解決?

    我正在嘗試在 wiced BLE 堆棧中使用基于演員的設計模式。 因此,所有任務都使用消息隊列相互通信。 消息隊列將保存塊大小為 64 的內存池指針的地址。 我維護著一個由這些池地址指針組成的隊列
    發(fā)表于 07-04 06:03

    RDMA簡介5之RoCE V2隊列分析

    操作中,只有RECEIVE操作會被添加到接收隊列。SEND/RECEIVE操作的完整流程,如圖1所示,首先由應用程序創(chuàng)建一個工作請求(WR),并將其提交到相應的工作
    發(fā)表于 06-05 17:28

    [Actor] 通過actor創(chuàng)建控制中心與數據采集工作站來看操作者架構

    何生產控制臺隊列的消息? 待續(xù)...... 對actor對象使用的“消息”的理解: 1、消息是一個類,它封裝了一個操作(方法.vi),包括該操作需要的數據。 actor對象的理解: 1、actor對象是一
    發(fā)表于 05-14 18:44

    [Actor]在程序框圖編程時,如何操作消息和方法對應的VI

    actor應用的關鍵: 當在程序框圖編程時,核心是針對“消息”vi 進行操作,并不操作消息對應的方法vi(具體要實現的功能)。 調用的消息vi時,后臺自動調用對應方法vi。 相當于在隊列狀態(tài)機中,
    發(fā)表于 05-13 18:10

    NVME控制器之隊列管理模塊

    隊列管理模塊是整個NVMe Host控制器的核心模塊,該模塊實現了提交隊列與完成隊列的管理,多隊列請求的仲裁判決等功能。隊列管理模塊中含有數
    發(fā)表于 05-03 20:19

    NVME控制器之隊列管理模塊

    隊列管理模塊是整個NVMe Host控制器的核心模塊,該模塊實現了提交隊列與完成隊列的管理,多隊列請求的仲裁判決等功能。隊列管理模塊中含有數
    的頭像 發(fā)表于 05-03 15:32 ?329次閱讀
    NVME控制器之<b class='flag-5'>隊列</b>管理模塊

    JavaWeb消息隊列使用指南

    在現代的JavaWeb應用中,消息隊列(Message Queue)是一種常見的技術,用于異步處理任務、解耦系統組件、提高系統性能和可靠性。 1. 消息隊列的基本概念 消息隊列是一種應用程序對應
    的頭像 發(fā)表于 11-25 09:27 ?766次閱讀

    探索字節(jié)隊列的魔法:多類型支持、函數重載與線程安全

    的數據結構,它能夠高效地存儲和管理數據流。通過使用字節(jié)隊列,我們可以靈活地處理不同類型的數據、確保數據的完整性,并在多線程環(huán)境中安全地進行操作。本文將深入探討字節(jié)
    的頭像 發(fā)表于 11-15 01:08 ?1406次閱讀
    探索字節(jié)<b class='flag-5'>隊列</b>的魔法:多類型支持、函數重載與線程安全

    為什么同一個隊列引用的全局變量,運行在兩個子vi中發(fā)現隊列數據丟失了

    我創(chuàng)建了一個隊列,然后將隊列引用做了個全局變量,運行在兩個子vi中,一個是只入隊列,另一個是只出隊列。但我發(fā)現,一個字vi數據入隊列成功,檢
    發(fā)表于 11-14 11:47