该类的主要目的是像事件总线一样,多个线程可以通过它注册/取消注册模型。
public class ThreadSafeHash {
private final HashMap<String, LinkedList<Model>> modelMap = new HashMap<>();
public boolean addModel(final String category, final Model model) {
LinkedList<Model> categoryContents = modelMap.get(category);
if (categoryContents == null) {
categoryContents = new LinkedList<>();
modelMap.put(category, categoryContents);
}
synchronized (categoryContents) {
return categoryContents.add(model);
}
}
public boolean removeModel(final Model model) {
LinkedList<Model> categoryContents = modelMap.get(model.getCategory());
if (categoryContents == null) {
return false;
}
synchronized (categoryContents) {
return categoryContents.remove(model);
}
}
}
主要的疑问是多个线程可能会将列表放到modelMap
但是此操作未同步,这是否会引入可见性风险?
答案 0 :(得分:3)
这不是线程安全的,因为对modelMap
的访问不同步 - 但不是因为可见性。
在addModel
方法中,两个线程都可以检测modelMap.get(category) == null
,因此他们都会将新的LinkedList
放入地图中 - 一个会踩到另一个,所以你会丢失在第一个线程中添加了model
实例。
这是因为“如果不存在则添加”检查不是原子的;而不是更新不可见。
答案 1 :(得分:3)
注意:此解决方案仅适用于Java 8或更高版本
另外我建议在这里使用ConcurrentHashMap.computeIfAbsent(..)
方法,如果它存在则通过键返回一个值并计算并返回一个新值(在你的情况下为new LinkedList<>()
)如果没有这样的值映射到密钥。整个方法都是原子的,如果你在LinkedList上使用Collections.synchronizedList()
,它将不再需要同步块。
public class ThreadSafeHash {
private final ConcurrentMap<String, List<Model>> modelMap = new ConcurrentHashMap<>();
public boolean addModel(final String category, final Model model) {
List<Model> categoryContents = modelMap.computeIfAbsent(
category,
k -> Collections.synchronizedList(new LinkedList<>());
return categoryContents.add(model);
}
public boolean removeModel(final Model model) {
List<Model> categoryContents = modelMap.get(model.getCategory());
return categoryContents != null && categoryContents.remove(model);
}
}
另请注意,ThreadSafeHash
类的API为其用户提供了使其不一致的机会。如果某人在addModel(..)
方法中为模型提供了错误的类别,则永远不会删除该模型,因为removeModel(..)
依赖于model.getCategory()
。
答案 2 :(得分:1)
HashMap不是线程安全的,并且该类不是线程安全的,因为它的访问权限未同步。
请记住,即使使用ConcurrentHashMap也不会给你100%&#34;安全&#34;反对并发问题,因为&#34; pufIfAbsent&#34;行为,由 categoryContents == null 和 categoryContents = new LinkedList&lt;&gt;()然后 modelMap.put(category,categoryContents)提供,是在任何情况下都不是原子的。