簡體   English   中英

盡管執行null檢查,但在多線程環境中仍獲取NullPointerException

[英]Getting NullPointerException in multi thread environment in spite of null check

我有一個名為statisticsCache的全局緩存,該緩存正在被多個線程同時修改和讀取。 即使在我應用了空檢查之后,但有時它會在加載運行中引發NullPointerException 詳情請見下文:

static Map<String, List<Statistics>> statisticsCache = new ConcurrentHashMap<String, List<Statistics>>();

// method to read the global  cache
List<Statistics> getStatisticsForQueue(String name) {
    List<Statistics> statsCopy = Collections.emptyList();
    List<Statistics> statistics = statisticsCache.get(name);
    if (statistics != null && !statistics.contains(null)) //Here is the check to avoid NPE but sometimes does not works
        statsCopy = new ArrayList<Statistics>(statistics);
    return statsCopy;
}

//method to write into global cache
private void setStatisticsListForQueue(String name) {
    // flushing all pending Last writes of buckets of a queue to DB
    flushStatisticToDB(name);
    if (!statisticsCache.containsKey(name)) {
        statisticsCache.put(name, new ArrayList<Statistics>(1));
    }
    List<Statistics> queueStatisticsList = queueServiceMetaDao
            .findStatisticsByname(name);
    if (queueStatisticsList != null && !queueStatisticsList.isEmpty()) { 
        for (Statistics statistic : queueStatisticsList) {
            // to avoid NPE
            if (statisticsCache.get(name).contains(statistic)) {
                statisticsCache.get(name).remove(statistic);
            }
            statisticsCache.get(name).add(statistic);
        }
    } else {
        statisticsCache.put(name, new ArrayList<Statistics>(1));
    }
}

//method where I am getting NPE 
public long getSize(String name) {
    long size = 0L;
    List<Statistics> statistics = getStatisticsForQueue(name);
    for (Statistics statistic : statistics) {
        size += statistic.getSize(); //Sometimes it throws NullPointerException
    }
    return size;
}

我應該采取什么預防性檢查來避免這種情況?

即使在我應用了空檢查之后,但有時它會在加載運行中引發NullPointerException

好的,因此,如果您有多個線程執行此代碼,則(IMO)最可能的解釋是代碼未正確同步。 當然,該映射本身是ConcurrentHashMap,因此應該是線程安全的。 但是,您有多個線程創建,訪問和修改ArrayLists而列表上沒有任何互斥或其他同步。

有很多事情可能出錯。 一種可能性是,一個線程從列表中刪除一個元素,而第二個線程同時在同一列表上調用getSize() 一個可能的結果是, getSize()中的迭代將看到列表大小的陳舊值,並返回一個已被另一個線程移除而為空的數組元素。 由於列表上兩個線程的操作沒有同步,因此,關於一個線程的列表更改對另一個線程的可見性,“所有賭注都已關閉”。

不管采用哪種確切的機制導致NPE,您在此處所做的操作都不能滿足必須保證滿足可預測行為的JLS要求(請參閱JLS 17.4 )。

我應該采取什么預防性檢查來避免這種情況?

您無法通過這種方式解決問題。 您需要在列表上進行正確的同步,以確保讀取和更新不會重疊。 您還需要使用putIfAbsent而不是if (! containsKey) { put ... }處理另一個競爭條件。

我認為statistic.getSize()可能為null,因此您嘗試這樣做:

size += statistic.getSize();

拋出NullPointerException

您應該檢查所有Statistics對象是否都具有其屬性“ size”!= null

問題實際上不是getSize()方法,因為long不能為null。 實際的NPE是這個

 List<Statistics> statistics = getStatisticsForQueue(name);

for (Statistics statistic : statistics) 

如果statistics為null則for循環將具有NPE。 因此,您可以避免發生的事情是

if(statistics != null)
  for (Statistics statistic : statistics) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM