ConcurrentHashMap作为带有synchronized的单倍缓存

时间:2017-07-28 14:09:51

标签: java android multithreading volatile concurrenthashmap

您如何看待我们是否需要使用synchronized块来更好地优化对Ad实例的访问? 可以从不同的线程中检索Ad.class的实例。 Synchronized通过ConcurrentHashMap中的一次get操作帮助一次获取实例。 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。

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

根据我的理解,您实际想要实现的是putIfAbsent,因此这比您做的更简单 (您正在使用双重检查锁定):

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