有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java多线程原子引用分配

我有一个使用simeple HashMap实现的缓存。就像-

HashMap<String,String> cache = new HashMap<String,String>();

此缓存大部分时间用于从中读取值。我有另一个方法可以重新加载缓存,在这个方法中,我基本上创建了一个新的缓存,然后分配引用。据我所知,对象引用的赋值在Java中是原子的

public class myClass {
     private HashMap<String,String> cache = null;
    public void init() {
       refreshCache();
    }
    // this method can be called occasionally to update the cache.
    public void refreshCache() {
        HashMap<String,String> newcache = new HashMap<String,String>();
       // code to fill up the new cache
       // and then finally
       cache = newcache; //assign the old cache to the new one in Atomic way
    }
}

我知道,如果我不将缓存声明为volatile,其他线程将无法看到更改,但我的用例将缓存中的更改传播到其他线程并不是时间关键,它们可以在较长时间内继续使用旧缓存

你看到线程问题了吗?考虑到许多线程都是从缓存读取的,并且只是在缓存被重新加载的时候。p>

编辑- 我的主要困惑是我不必在这里使用原子引用,因为赋值操作本身就是原子的

编辑- 我明白,为了使排序正确,我应该将缓存标记为volatile。 但如果refreshCache方法被标记为synchronized,我就不必将缓存设置为volatile,因为synchronized block将负责排序和可见性


共 (4) 个答案

  1. # 1 楼答案

    那么CopyOnWrite呢。。收藏:

    java.util.concurrent.CopyOnWriteArraySet
    java.util.concurrent.CopyOnWriteArraySet
    and org.apache.mina.util.CopyOnWriteMap
    

    它们可以很好地匹配您的情况,并且线程安全

  2. # 2 楼答案

    如果没有适当的记忆屏障,它是不安全的

    有人会认为缓存的分配(cache=newCache)会在填充缓存的步骤之后发生。但是,其他线程可能会因这些语句的重新排序而受到影响,因此赋值可能会在填充缓存之前发生。因此,可以在新缓存完全构建之前获取它,或者更糟的是,可以看到ConcurrentModificationException

    您需要强制执行“发生在之前”关系以防止这种重新排序,而将缓存声明为volatile将实现这一点

  3. # 3 楼答案

    您应该将缓存标记为volatile

    虽然您注意到其他线程可能会在“很长一段时间”内继续使用过时的缓存,但您应该注意到,如果没有同步边缘,它们可能会永远继续使用过时的缓存。这可能不是人们想要的行为

    优先顺序(主要是由于可读性):

    • 以同步方法更新字段
    • 使用AtomicReference<Map<>>
    • 使用volatile

    另见本question

  4. # 4 楼答案

    看起来还可以。确保不太频繁地调用refreshCache,或将其标记为synchronized