【问题标题】:Java multi-threading atomic reference assignmentJava 多线程原子引用赋值
【发布时间】:2011-03-15 02:53:19
【问题描述】:

我有一个使用简单 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,其他线程将无法看到更改,但我的用例将缓存中的更改传播到其他线程并不是时间关键,它们可以继续工作长时间使用旧缓存。

您是否发现任何线程问题?考虑到许多线程正在从缓存中读取数据,并且只是有时会重新加载缓存。

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

编辑 - 我知道为了使排序正确,我应该将缓存标记为易失性。 但是如果 refreshCache 方法被标记为已同步,我不必将缓存设置为 volatile,因为同步块将负责排序和可见性?

【问题讨论】:

  • 为了简单的缓存目的,我通常使用 ConcurrentHashMap 并且不用担心锁定。我通常不在乎(引用透明的)计算是否发生两次(比如说因为线程 B 开始计算与线程 A 当前正在计算的值相同的值,因此还没有放入缓存中)。在你的情况下,我也会将 cache 设为 volatile。但我没有看到“刷新缓存”的意义。让你的缓存成为 LRU/MRU,然后简单地向它添加新的缓存值,而不是“重置”它。
  • 至少使用新映射刷新缓存允许使用非阻塞缓存映射实现

标签: java multithreading


【解决方案1】:

如果没有适当的内存屏障,它是安全的。

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

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

【讨论】:

  • 这是一个很好的建议。我想到了这个问题,感谢您清除它。如果订单被修改,那肯定是一场灾难。
  • 如果我将 refreshCache- 设为同步方法会怎样?
  • 如果您想确保在给定时间只有一个线程可以更改数据,您可能希望使其同步。但是,我假设读取器线程不会参与锁定(它们通过不同的方法来读取值?),在这种情况下,单独的同步不会为您提供保护。使缓存字段易失是实现先发生关系的有效方法。
  • 是的,读取线程不参与锁定,它们通过不同的方法读取值。我必须与其他写入器线程同步到缓存,但你是对的,我仍然需要 volatile 来保护排序。
  • 不错的答案,但我只想添加一些必读内容:cs.umd.edu/~pugh/java/memoryModel
【解决方案2】:

您应该将缓存标记为volatile

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

按优先顺序排列(主要是因为可读性):

  • 以同步方法更新字段
  • 使用AtomicReference&lt;Map&lt;&gt;&gt;
  • 使用volatile

另请参阅question

【讨论】:

    【解决方案3】:

    CopyOnWrite.. 集合怎么样:

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

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

    【讨论】:

      【解决方案4】:

      似乎还可以。确保refreshCache 没有被太频繁地调用或标记为synchronized

      【讨论】:

        猜你喜欢
        • 2012-05-26
        • 2017-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多