Непоследовательные ответы при использовании ConcurrentHashMap в многопоточной среде - PullRequest
0 голосов
/ 11 октября 2018

У нас есть одна тема, которая регулярно обновляет карту.И затем у нас есть несколько других потоков, которые читают эту карту.

Вот как выполняется поток обновления

private Map<String, SecondMap> firstMap = new ConcurrentHashMap<>();

private void refresh() //This method is called every X seconds by one thread only
{
   List<SecondMap> newData = getLatestData();
   final List<String> newEntries = new ArrayList<>(); 
   for(SecondMap map : newData) {
       newEntries.add(map.getName());
       firstMap.put(map.getName(), map); 
   }
   final Set<String> cachedEntries = firstMap.keySet();
   for (final String cachedEntry : cachedEntries) {
       if (!newEntries.contains(cachedEntry)) {
           firstMap.remove(cachedEntry);
       }
   } 
}

public Map<String, SecondMap> getFirstMap()//Other threads call this
{
    return firstMap;
}

Класс SecondMap выглядит следующим образом

class SecondMap {
    Map<String, SomeClass> data; //Not necessarily a concurrent hashmap
    public Map<String, SomeClass> getData() {
        return data;
    }
}

Ниже приведена упрощенная версия доступа потоков читателя

public void getValue() {
    Map<String, SecondMap> firstMap = getFirstMap();
    SecondMap secondMap = firstMap.get("SomeKey");
    secondMap.getData().get("AnotherKey");// This returns null
}

Мы видим, что в других потоках, когда они перебирают полученный firstMap, иногда они получают значения null для некоторых ключей в SecondMap.Мы не видим значений null для ключей в firstMap, но видим значения null для ключей во втором значении.Единственное, что мы можем исключить, это то, что метод getLatestData никогда не вернет такие данные.Он читает из базы данных и возвращает эти записи.Во-первых, в базе данных никогда не может быть нулевых значений.Также мы видим, что это случается иногда.Вероятно, нам здесь чего-то не хватает при правильной обработке многопоточной ситуации, но я ищу объяснение, почему это может произойти.

1 Ответ

0 голосов
/ 13 октября 2018

Если предположить, что Map<String, SomeClass> data; внутри класса SecondMap равно HashMap, вы можете получить нулевое значение для ключа в двух сценариях.1. Если ключ соответствует нулевому значению.Пример "Something" -> null.2. Если ключ не находится на карте в первую очередь.

Так что, не зная много о том, откуда поступают данные.Если одна из карт, возвращаемых getLatestData();, вообще не имеет ключа «SomeKey» на карте, она вернет null.

Кроме того, поскольку недостаточно информации о том, как это Map<String, SomeClass> data;обновленный, и если он изменчив или неизменен, у вас могут быть проблемы там.Если эта карта неизменна, а SecondMap неизменна, то, скорее всего, все в порядке.Но если вы изменяете if из нескольких потоков, вы должны сделать его ConcurrentHashMap, и если вы обновите ссылку на новый Map<String, SomeClass> data из других потоков, внутри SecondMap вы также должны сделать эту ссылку volatile.

class SecondMap {
    volatile Map<String, SomeClass> data; //Not necessarily a concurrent hashmap
    public Map<String, SomeClass> getData() {
        return data;
    }
}

Если вы хотите глубже понять, когда использовать ключевое слово volatile и все тонкости гонок данных, в этом онлайн-курсе есть раздел https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY об этом.Я не видел ни одного ресурса, который бы объяснял и демонстрировал это лучше.И, к сожалению, в Интернете так много статей, которые просто объясняют это НЕПРАВИЛЬНО, что печально.

Я надеюсь, что из небольшой информации в этом вопросе я смог указать вам некоторые направления, которые могут помочь.Пожалуйста, поделитесь дополнительной информацией, если ничего из этого не работает, или если что-то работает, пожалуйста, дайте мне знать, мне интересно знать, что это было :)

...