2011-03-15 86 views
18

我有一个缓存,我使用simeple HashMap实现。像 -Java多线程原子参考分配

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

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

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 
    } 
} 

我明白,如果我不申报缓存中挥发,其他线程将无法看到的变化,但它不是时间关键我使用的情况下传播的高速缓存给其他线程,它们的变化可以继续使用旧缓存以延长时间。

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

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

编辑 - 我明白,要使排序正确,我应该将缓存标记为volatile。 但是,如果refreshCache方法标记为synchronized,那么我不必将缓存设置为volatile,因为Synchronized块会照顾排序和可见性?

+0

为了简单的缓存目的,我通常使用* ConcurrentHashMap *并且不用担心锁定。我通常不关心一个(引用透明的)计算是否发生两次(也就是说,因为线程B开始计算与线程A当前正在计算的值相同的值,因此尚未放入缓存)。在你的情况下,我还会让* cache *成为* volatile *。但我没有看到“刷新缓存”的重点。使你的缓存成为一个LRU/MRU,并简单地向它添加新的缓存值,而不是“重置”它。 – SyntaxT3rr0r 2011-03-15 05:32:15

+0

至少使用新映射刷新缓存允许使用非阻塞缓存映射实现 – ThomasRS 2012-04-19 11:31:52

回答

27

这是不是安全没有适当的内存屏障。

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

您需要强制执行happen-before关系来防止这种重新排序,并且将缓存声明为volatile将实现此目的。

+1

这是一个很好的建议。我有这个问题,并感谢清除它。如果订购被修改,那么它肯定是一场灾难。 – Shamik 2011-03-15 15:19:37

+0

如果我使refreshCache成为同步方法,会发生什么情况? – Shamik 2011-03-15 19:26:57

+0

如果要确保只有一个线程可以在给定时间更改数据,那么您可能希望使其同步。但是,我假设读者线程不会参与锁定(他们通过不同的方法来读取值),在这种情况下,同步本身并不会给您提供保护。使缓存字段变为易失性是实现事前 - 事前关系的有效方式。 – sjlee 2011-03-15 19:37:48

-1

似乎没问题。请确保refreshCache不会过于频繁地被调用或标记为​​。

4

您应该将缓存标记为volatile

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

在优选方案(主要是由于可读性)的顺序:

  • 更新的同步方法
  • 使用AtomicReference<Map<>>
  • 使用volatile

参见本question领域。

0

CopyOnWrite怎么样..收藏:

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

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