我正在将Spring与Redis一起使用,并且正在使用哈希中的列表。一切都在一个线程上运行良好,当我不得不用多个实例更新列表值时,问题就来了。
这是我的代码,可从哈希中获取价值:
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);
[当有多个实例时,我会出现种族状况。有没有办法以原子方式更新列表值?
您当前的实现不好,因为您在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);
}
您可以修改put()
方法以使其同步。这样,所有调用put()
的线程都将首先需要获得单个共享锁(在下面的示例中,它将在this
上)。如果两个线程尝试更新Redis中的相同密钥,那么一个线程将不得不等待另一个线程完成。