【问题标题】:ConcurrentHashMap as singletone cache with synchronizedConcurrentHashMap 作为带同步的单例缓存
【发布时间】:2017-07-28 14:09:50
【问题描述】:

您如何看待,我们是否需要使用同步块来更好地优化对 Ad 实例的访问? Ad.class 的实例可以从不同的线程中检索。同步有助于通过 ConcurrentHashMap 的一次获取操作一次获取一个实例。 ConcurrentHashMap 将所有值存储为 volatile。我在 java 1.7 for android 上使用它,computeIfAbsent 在 java 1.8 中可用。

很高兴得到详细的答案,为什么不或为什么是。 谢谢!

public final class Ad {

    private final static Map<String, Ad> ads = new ConcurrentHashMap<>();

    public static Ad get(@NonNull String appId) {
        if (appId == null) appId = "";

        boolean containsAd = ads.containsKey(appId);

        Ad localInstance = containsAd ? ads.get(appId) : null;

        if (localInstance == null) {
            synchronized (Ad.class) {

                containsAd = ads.containsKey(appId);

                localInstance = containsAd ? ads.get(appId) : null;

                if (localInstance == null) {
                    localInstance = new Ad();
                    localInstance.setAdId(appId);
                    ads.put(appId, localInstance);
                }
            }
        }
        return localInstance;
    }

    private Ad() {
    }
}

更新:感谢大家的帮助。我将 ConcurrentHashMap 替换为 HashMap。

【问题讨论】:

  • 如果你阅读了javadocs,这个类被设计用来防止锁定。同步块没有意义。您不妨使用哈希表
  • 是的,如果您发现自己同时使用了synchronizedConcurrentHashMap,那么您要么没有正确使用ConcurrentHashMap,要么您应该使用非线程安全的集合。

标签: java android multithreading volatile concurrenthashmap


【解决方案1】:

这不是很理想。如果多个线程同时尝试初始化值,那么它们将相互阻塞,即使它们正在寻找不同的键。

您应该使用ConcurrentHashMap.computeIfAbsent 在一个步骤中检查添加并创建缺失的。这样您就不会创建任何未使用的广告,并且两个线程只会在尝试初始化同一条目时相互阻塞:

public static Ad get(@NonNull String appId) {
    if (appId == null) appId = "";

    return ads.computeIfAbsent(appId, Ad::new);
}

private Ad(String appId) {
    this();
    setAdId(appId);
}

【讨论】:

  • 谢谢@matt-timmermans,这是有道理的。这是否意味着当我在一个线程中创建新广告并将其放入 ConcurrentHashMap 以获取当前键时,其他线程将被阻塞?对不起,我忘了写我在java 1.7 for android上使用它。 computeIfAbsent 在 java 1.8 中可用(((
  • 只有试图创建广告的线程(在相同的或不同的键上)才会被阻止,所以如果没有太多的键也不错跨度>
【解决方案2】:

据我了解,您真正想要实现的是putIfAbsent,因此这比您所做的要简单得多(您正在使用双重检查锁定):

public static Ad get(String appId) {
    String newId = appId == null ? "" : appId;
    ads.putIfAbsent(newId, new Ad());
    return map.get(newId);
}

【讨论】:

  • 使用此代码,如果地图没有appId 的映射,Ad.get() 将返回 null。我认为这并不理想。
  • 如果newId在地图中不存在,putIfAbsent会返回null,你的get()方法也会返回null
  • @SeanBright 哦,该死!我在想computeIfAbsent(newId, s -&gt; new Ad()),但写了一个完全不同的东西——谢谢你纠正我。但这显然不再是原子的了
  • 现在您在putIfAbsentget 之间有一个竞争条件。如果从未调用过removeclear,那可能没问题,但请记住这一点。
  • 是的,抱歉,我在您的评论中错过了这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
  • 1970-01-01
  • 2010-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多