2015-12-02 2 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

Возможный дубликат [Что такое Исключение Null Указатель и как его исправить?] (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 будет bealways false, – Laxmikant

ответ

0

Я думаю, что statistic.getSize() может быть пустым, так что вы пытаетесь сделать:

size += statistic.getSize(); 

Который бросает NullPointerException

Вы должны проверить, если все объекты Статистика имеют свое свойство «размер» ! = null

+0

, но размер длинный. Почему это может дать NPE. – Laxmikant

+0

@ user2492242 Если используется 'Long', это может быть« null », поэтому, если ваш' Statistics.getSize() 'имеет тип возврата' Long' и он никогда не устанавливается, он может быть «null» и действительно вызовет NPE , – Ian2thedv

+0

но не длинный публичный long getCount() { кол-во возвратов; } public void setCount (long count) { this.count = count; } public long getSize() { return size; } public void setSize (длинный размер) { это.размер = размер; } – Laxmikant

0

Проблема на самом деле не getSize() метод, так как долго не может быть пустым. Фактически NPE это

List<Statistics> statistics = getStatisticsForQueue(name); 

for (Statistics statistic : statistics) 

Если статистика null для цикла будет иметь NPE. Так что вы можете сделать, чтобы избежать этого является

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

Я также считаю, что статистика равна нулю, но, как вы можете видеть в getStatisticsForQueue, я уже применил превентивный chek: if (статистика! = Null &&! Statistics.contains (null)) // Здесь приведена проверка, чтобы избежать NPE но иногда не работает – Laxmikant

+0

Я предполагаю, что проверка статистики.contains (null) проверяет, нет ли нулевой записи в кеше – Laxmikant

2

Даже после того, как я применил нулевой чек, но когда-то он бросает NullPointerException в перспективе нагрузки.

ОК, поэтому, если у вас есть несколько потоков, выполняющих этот код, то (ИМО) наиболее вероятным объяснением является то, что код не синхронизируется должным образом. Конечно, сама карта является ConcurrentHashMap, поэтому она должна быть потокобезопасной. Тем не менее, у вас есть несколько потоков, создающих, доступ и изменение ArrayLists без какого-либо взаимного исключения или другой синхронизации в списках.

Существует множество вещей, которые могут пойти не так. Одна из возможностей заключается в том, что один поток удаляет элемент из списка, а второй поток одновременно вызывает getSize() в том же списке. Один из возможных результатов состоит в том, что итерация в getSize() увидит устаревшее значение размера списка и вернет элемент массива, который был отменен удалением другого потока. Поскольку в списке нет синхронизации операций двух потоков, «все ставки отключены» относительно видимости изменений одного потока в другом потоке.

Независимо от того, какой именно механизм ведет к NPE, то, что вы здесь делаете, не соответствует требованиям JLS (см. JLS 17.4), которые должны быть выполнены, чтобы гарантировать предсказуемое поведение.

Какую профилактическую проверку следует применять, чтобы избежать этого?

Вы не можете решить проблему именно так. Вам нужна правильная синхронизация в списках, чтобы гарантировать, что чтения и обновления не могут пересекаться. Вам также необходимо использовать putIfAbsent, а не if (! containsKey) { put ... }, чтобы иметь дело с другим состоянием гонки.

Смежные вопросы