Как обрабатывать параллельные обновления и чтения в POJO, хранящемся на карте

Я планирую реализовать простой кэш в памяти для поиска с использованием CHM, который работает не более чем с двумя параллельными потоками. Один поток выполняет итерацию и обновление CHM с использованием итератора, а второй поток читает значения из карты.

Как показывает мое понимание, и то, что я испытывал до сих пор, CHM Iterator является отказоустойчивым, что означало бы, что итерация произойдет на снимке данных.

Итак, давайте предположим, что Поток A извлекает значение из CHM, которое является POJO, используя ключ, и выполняет итерацию / обновление POJO. В то же время ThreadB получает тот же POJO. Так, каково было бы ожидаемое поведение в этой точке?

  • Будет ли ThreadB видеть обновления, которые делает Thread A? Я думаю, нет, потому что Thread A все еще находится в процессе обновления.
  • Если да, пожалуйста, поделитесь своими мыслями, как это произойдет?
  • Если нет, предложите эффективные альтернативные способы, если вы их реализовали.

POJO что-то вроде ниже.

Class Pojo{
    private volatile long a;
    private volatile long b;
    ....    

    public long getA() {
        return this.a;
    }

    void setA(long a) {
        this.a = a;
    }
}

3 ответа

Как показывает мое понимание, и то, что я испытывал до сих пор, CHM Iterator является отказоустойчивым, что означало бы, что итерация произойдет на снимке данных.

Это не правильно. Согласно документации:

Точно так же, Iterators, Spliterators и Enumerations возвращают элементы, отражающие состояние хеш-таблицы в некоторой точке во время или после создания итератора / перечисления.

[акцент мой]


Итак, давайте предположим, что Поток A извлекает значение из CHM, которое является POJO, используя ключ, и выполняет итерацию / обновление POJO. В то же время ThreadB получает тот же POJO. Так, каково было бы ожидаемое поведение в этой точке?

Если Поток A изменяет POJO, а Поток B проверяет тот же POJO, то ConcurrentHashMap вообще не имеет значения: не имеет большого значения, как два потока получили POJO, только то, как сам POJO обрабатывает параллельные обновления и чтения.

Вы ничего не сказали нам о классе POJO, но если он не был тщательно спроектирован, чтобы разрешать атомарные обновления и чтения, то более или менее очевидно, что поток B иногда будет просматривать POJO в несовместимом состоянии с некоторыми потоками. А читает, но не все из них.

Ну, ты не можешь сделать что-то подобное.

Pojo myPojo = new Pojo();
myPojo.setA(10);
myPojo.setB(11);

// Atomic replace
CHM.replace(key, myPojo);

Замена CHM является атомарной, и никакой другой поток не будет считать устаревшее значение POJO, пока выполняется замена.

  • Значение вашего CHM должно быть AtomicReference
  • Сделайте класс Pojo неизменным, обновление любого нового значения должно создать новый объект Pojo.
  • Реализуйте конструктор, который принимает все параметры в конструкторе.
  • Реализуйте каждый метод set, как пример, приведенный ниже
public Pojo setA(int newA ){
    Pojo newPojo = new Pojo(newA, this.getB());
    return newPojo;
}
  • При обновлении CHM следуйте инструкциям
AtomicReference pojoRef = CHM.get(Key);
Pojo oldPojo = pojoRef.get();
Pojo newPojo = oldPojo.setA(10);
while(!pojoRef.compareAndSet(oldPojo, newPojo) ){
    AtomicReference pojoRef = CHM.get(Key);
    Pojo oldPojo = pojoRef.get();
    Pojo newPojo = oldPojo.setA(10);
}
Другие вопросы по тегам