我需要知道何时应该在使用ConcurrentHashMap时向我的代码添加一些同步块.假设我有一个类似的方法:
private static final ConcurrentMap<String, MyObjectWrapper> myObjectsCache = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY);
public List<MyObject> aMethod(List<String> ids, boolean b) {
List<MyObject> result = new ArrayList<>(ids.size());
for (String id : ids) {
if (id == null) {
continue;
}
MyObjectWrapper myObjectWrapper = myObjectsCache.get(id);
if (myObjectWrapper == null) {
continue;
}
if (myObjectWrapper.getObject() instanceof MyObjectSub) {
((MyObjectSub) myObjectWrapper.getObject()).clearAField();
myObjectWrapper.getObject().setTime(System.currentTimeMillis());
}
result.add(myObjectWrapper.getObject());
if (b) {
final MyObject obj = new MyObject(myObjectWrapper.getObject());
addObjectToDb(obj);
}
}
return result;
}
我该如何有效地使这个方法并发?
我认为“get”是安全的但是一旦我从缓存中获取值并更新缓存对象的字段 – 可能会出现问题,因为另一个线程可以获得相同的包装并尝试更新相同的底层对象…我应该添加吗同步?如果是这样,那么我应该从“get”到循环迭代结束还是整个循环同步?
也许有人可以分享一些更具体的指导方针,当需要对循环内的地图键/值等进行更多操作时,正确有效地使用ConcurrentHashMap …
我真的很感激.
编辑:
该问题的一些背景:
我目前正致力于在生产代码中重构一些dao类,并且一些类使用HashMaps来缓存从数据库中检索的数据.使用缓存(用于写入或读取)的所有方法都将其整个内容放在同步(缓存)块中(播放安全吗?).我没有太多的并发经验,我真的想借此机会学习.我天真地将HashMaps更改为ConcurrentHashMaps,现在想要删除必要的同步bloocks.所有缓存都用于写入和读取.所提出的方法基于我已经改变的方法之一,现在我正在尝试学习何时以及在何种程度上同步.方法clearAField只更改包装的POJO对象的一个字段的值,addObjectToDb尝试将对象添加到数据库.
其他示例是重新填充缓存:
public void findAll() throws SQLException{
// get data from database into a list
List<Data> data=getAllDataFromDatabase();
cacheCHM.clear();
cacheCHM.putAll(data);
}
在这种情况下,我应该将clear和putAll放在一个synchronize(cacheCHM)块中,对吗?
我试图找到并阅读一些关于CHM的正确和有效使用的帖子/文章,但大多数处理单个操作,没有循环等….我发现的最好的是:
http://www.javamadesoeasy.com/2015/04/concurrenthashmap-in-java.html
最佳答案 你没有提到你期望在你的应用程序中发生什么并发,所以我假设你有多个线程调用aMethod,没有别的.
你只需要一次调用ConcurrentHashMap:myObjectsCache.get(id),这很好.事实上,因为没有任何东西将数据写入你的objectCache [见上面的假设]你甚至不需要ConcurrentHashMap!你可以使用任何不可变的集合.你最后有一个可疑的行:addObjectToDb(obj),这个方法是否也会影响你的缓存?如果是这样它仍然是安全的(可能,我们必须看到方法是确定的),但你肯定需要ConcurentHashMap.
危险在于您更改对象的位置,此处:
myObjectWrapper.getObject().clearAField();
myObjectWrapper.getObject().setTime(System.currentTimeMillis());
多个线程可以同时在同一个对象上调用这些方法.不知道这些方法做了什么,我们不能说这是否安全.如果这些方法都标记为同步,或者您注意确保这些方法同时运行是安全的,那么您就可以了(但请注意,这些方法的范围可以按照您可能直观预期的不同顺序运行! ).如果您不那么小心,那么可能存在数据损坏.