Я использую Spring с Redis и работаю со списком внутри хеша. Все отлично работает в одном потоке, возникают проблемы, когда мне приходится обновлять значение списка несколькими экземплярами.

Вот мой код для установки и получения значения из Hash:

public void put(String hashName, int key , List<myObj> myList) {
        redis.opsForHash().put(hashName, String.valueOf(key), myList);
        }

public List<myObj> get(String hashName, string key) {
    Object map = redis.opsForHash().get(hashName,key);
    if (map==null) {
        log.info("no keys found");
        return new ArrayList<myObj>();
    }
    List<myObj> myList= mapper.convertValue(map, new TypeReference<List<myObj>(){});
    return myList;
}

Что я делаю, чтобы выполнить обновление:

List<myObj> myList= hash.get(hashName,key);
myList.add(obj);
hash.put(hashName, key, myList);

Когда есть больше чем один случай, я нахожусь в состоянии гонки. Есть ли способ обновить значения списка атомарным способом?

2
LucaBrg 20 Дек 2019 в 21:37

1 ответ

Лучший ответ

Ваша текущая реализация не подходит, потому что в put() вы обновляете весь список. Если многие потоки хотят добавить один элемент в список, они сначала получают текущий список, затем добавляют элемент, а затем помещают новый список. Каждый поток отменяет результат предыдущего, последний побеждает. Использование synchronized здесь не имеет значения.

< Сильный > Решение

Не заменяйте весь список. Вместо этого добавьте один элемент в список. Удалите метод put() и добавьте новый, например:

public synchronized void add(String hashName, int key, myObj element) {
    List<myObj> myList;
    Object map = redis.opsForHash().get(hashName,key);
    if (map != null) {
        myList= mapper.convertValue(map, new TypeReference<List<myObj>(){});
    } else {
        myList = new ArrayList<myObj>();
    }
    myList.add(element);
    redis.opsForHash().put(hashName, String.valueOf(key), myList);
}

Кроме того, убедитесь, что нет попыток изменить список напрямую и что единственный способ добавить элементы - это использовать ваш метод add(). Используйте Collections.unmodifiableList():

public List<myObj> get(String hashName, string key) {
    Object map = redis.opsForHash().get(hashName,key);
    if (map==null) {
        log.info("no keys found");
        return new ArrayList<myObj>();
    }
    List<myObj> myList= mapper.convertValue(map, new TypeReference<List<myObj>(){});
    return Collections.unmodifiableList(myList);
}
1
mentallurg 23 Дек 2019 в 00:08