如何在Java中创建一个线程安全的一次性read-many Map?

时间:2012-04-14 10:04:28

标签: java thread-safety

我有一个带有私有静态映射的Java类,用于在执行应用程序期间存储信息。我只会将一次键/值放入Map中,但可以多次读取地图值。

所以我现在拥有它的方式,代码执行get并检查null。如果为null,那么我收集我需要的数据并将其放入地图中。客户端代码的后续调用将保证从映射中获取值。客户端不需要进行空检查。

这样做的原因是将数据放入地图可能很昂贵,所以我只想每个键执行一次。

这有什么模式吗?我似乎无法找到讨论这种情况的任何内容。

TIA

这是一个完全非线程安全的例子:

public class TestWorm {

    private static Map<String, Object> map = new HashMap<String, Object>(32);

    public Object getValue(String key) {
        if (map.get(key) != null) {
            return map.get(key);
        }

        // do some process to get Object
        Object o = new Object();
        map.put(key, o);
        return o; 
    }
}

3 个答案:

答案 0 :(得分:3)

你最好的选择是ConcurrentHashMap,它是putIfAbsent方法。

您的实现不是线程安全的。为了使线程安全,声明字段final,将实现类更改为ConcurrentHashMap,这就足够了,如果您不关心有时值是否会被计算并存储多次(这种情况很少见:如果两个线程同时输入get和相应的值尚未计算。这通常是很好的权衡,因为通常最常见的情况是当您在缓存中有某些东西时。在这种情况下,您不使用任何额外的同步来检索现有值。)

如果要确保应用程序中存在至多一个给定键的值,您可以使用putIfAbsent而不是put来进一步扩展您的实现。

实现此目的的另一种方法是使用guava库:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/MapMaker.html

另一种方法是使用ConcurrentHashMapV8的computeIfAbsent(http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?view=markup有一天会出现在Java 8中。

答案 1 :(得分:2)

checkout ConcurrentHashMap.putIfAbsent(),它将提供您同步存储和检索所需的一切。

答案 2 :(得分:0)

这是一种典型的缓存模式。

例如Spring Framework offers a good API since 3.1

但是那里也有专门的框架,例如Ehcache