如何在Java中创建线程安全的一次写多读映射?

如何在Java中创建线程安全的一次写多读映射?,java,thread-safety,Java,Thread Safety,我有一个带有私有静态映射的Java类,用于在应用程序执行期间存储信息。我只会在地图中输入一次键/值,但地图值可能会被读取很多次 按照我现在的方式,代码执行get并检查null。如果为空,则收集所需数据并将其放入地图中。客户端代码的后续调用将保证从映射中获取值。客户端不需要执行空检查 这样做的原因是,将数据放入地图可能会很昂贵,因此我只希望每个键执行一次 这有什么模式吗?我似乎找不到任何讨论这种情况的东西 短暂性脑缺血发作 下面是一个完全非线程安全的示例: public class TestWor

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

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

这样做的原因是,将数据放入地图可能会很昂贵,因此我只希望每个键执行一次

这有什么模式吗?我似乎找不到任何讨论这种情况的东西

短暂性脑缺血发作

下面是一个完全非线程安全的示例:

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; 
    }
}
公共类测试蠕虫{
私有静态映射=新HashMap(32);
公共对象getValue(字符串键){
if(map.get(key)!=null){
返回map.get(key);
}
//执行一些过程来获取对象
对象o=新对象();
地图放置(键,o);
返回o;
}
}

签出,它将提供同步存储和检索所需的所有内容。

您最好的选择是ConcurrentHashMap及其putIfAbsent方法

您的实现不是线程安全的。为了使其线程安全,请声明fieldfinal,将实现类更改为ConcurrentHashMap,如果您不关心有时是否会多次计算和存储值,那么这就足够了(这种情况很少见:如果两个线程同时输入get,并且还没有计算出相应的值。这通常是一种很好的折衷方法,因为通常最常见的情况是当缓存中有某些内容时。在这种情况下,您不使用任何额外的同步来检索现有值。)

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

另一种实现方法是使用guava库:


另一种方法是使用ConcurrentHashMapV8的ComputeFabSent(http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?view=markup)这是一种典型的缓存模式

例如


但是那里也有类似的专门框架。

当你做一个get()时,你只需要做一次。@sprokeyboy:看起来你需要的正是番石榴。是的,如果你想添加的对象不贵,那么使用bsent就可以了。我需要这样做的原因是我不想每次都要做昂贵的工作来为地图创建对象。真的吗?你认为我应该在我的项目中添加一个完整的框架或库来创建一个基本模式吗?@Dan Howard我刚才试着回答你关于这是什么模式的问题。当然,一个小项目不会从框架中获得任何好处。如果在一个大型项目中,这些地图访问总体上是分散的,那么可以将它们放在像ehcache这样的框架中。