2015-12-02 50 views
0

我有一個名爲statisticsCache的全局緩存,它正在被多個線程同時修改和讀取。即使在我申請了空檢查但是有時它會在加載運行中拋出NullPointerException。詳見下文:在多線程環境中儘管空檢查得到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; 
} 

我應該採用什麼預防性檢查來避免這種情況?

+0

可能重複[什麼是空指針異常,以及如何解決它?](http://stackoverflow.com/questions/218384/what-is-a-null-pointer-exception-and-how -do-i-fix-it) –

+0

嘗試'size + = statistic.getSize()== null? 0L:statistic.getSize()' – Ian2thedv

+0

大小很長,但不長。如此統計。getSize()== null將永遠爲假, – Laxmikant

回答

0

我認爲statistic.getSize()可能爲空,所以你正在嘗試做的:

size += statistic.getSize(); 

會拋出一個NullPointerException

你應該檢查所有統計對象都有自己的財產「大小」 != null

+0

但大小很長。它怎麼能給NPE。 – Laxmikant

+0

@ user2492242如果使用'Long',它可以是'null',所以如果你的'Statistics.getSize()'具有返回類型'Long'並且它永遠不會被設置,它可能是'null'並且確實會導致一個NPE 。 – Ian2thedv

+0

但它不是long long getCount(){ return count; } public void setCount(long count){ this.count = count; } public long getSize(){ return size; } public void setSize(long size){this.style.width = size; } } – Laxmikant

0

問題實際上不是getSize()方法,因爲long不能爲空。實際NPE是這樣

List<Statistics> statistics = getStatisticsForQueue(name); 

for (Statistics statistic : statistics) 

如果統計是null for循環將會有一個NPE。所以,你可以做些什麼來避免這種情況是

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

我也認爲統計量爲null,但正如你可以在getStatisticsForQueue中看到的,我已經應用了一個預防性的chek:if(statistics!= null &&!statistics.contains(null))//這裏是避免NPE但有時不起作用 – Laxmikant

+0

我假設!statistics.contains(null)檢查將確保高速緩存中沒有空條目 – Laxmikant

2

即使我已經申請零檢查,但有時它在負荷運行拋出NullPointerException

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

有很多事情可能會出錯。一種可能是一個線程從列表中刪除一個元素,並且第二個線程同時在同一列表上調用getSize()。一個可能的結果是,getSize()中的迭代將看到列表大小的陳舊值,並返回一個數組元素,該數組元素已被另一個線程的刪除消除。由於列表上兩個線程的操作沒有同步,所以關於一個線程列表的可見性更改爲另一個線程的「所有下注都關閉」。

無論什麼確切的機制導致NPE,您在這裏所做的都不符合JLS要求(見JLS 17.4)必須滿足以保證可預測的行爲。

我應該使用什麼預防性檢查來避免這種情況?

你不能這樣解決問題。您需要在列表上進行適當的同步,以確保讀取和更新不會重疊。您還需要使用putIfAbsent而不是if (! containsKey) { put ... }來處理另一種競爭條件。