此Java类是否具有可见性风险或线程安全性?

时间:2016-02-03 08:45:26

标签: java multithreading

该类的主要目的是像事件总线一样,多个线程可以通过它注册/取消注册模型。

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但是此操作未同步,这是否会引入可见性风险?

3 个答案:

答案 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)提供,是在任何情况下都不是原子的。

ConcurrentMap.putIfAbsent