【发布时间】:2026-01-18 19:20:03
【问题描述】:
在使用 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,现在想在需要的地方删除同步块。所有缓存都用于写入和读取。所提出的方法基于我已更改的方法之一,现在我正在尝试了解何时以及在何种程度上同步。 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
【问题讨论】:
-
这在很大程度上取决于您的应用程序逻辑。
-
如果您需要更新检索到的值以实现线程安全,那么您必须在对象本身上进行同步。
ConcurrentHashMap只保护映射本身的结构(即键与值的关系),而不是包含的值。 -
我同意@Jim,并发映射只会保护结构(即键与值的关系)。我想再添加一件事,根据上面的代码上下文,您只读取值,即 myObjectsCache.get(id),因此为此您甚至可能不需要并发映射,直到您调用map.put().
-
所以我应该使用 synchronize(myObjectWrapper) 块并将包装器上的所有操作放入其中,或者我是否还需要将从缓存中检索对象放入同步块中?还是整个循环?但是,从我读到的内容来看,您应该尽可能少地使用同步块,所以最后一个不是一个好主意,对吧?
标签: java multithreading concurrenthashmap