java同步缓存上的批量操作

java同步缓存上的批量操作,java,multithreading,caching,Java,Multithreading,Caching,我想实现一个简单的缓存,它会定期更新,每次更新都会触发完全缓存清除和数据插入 伪代码: //context calls periodically this method cache.clear(); cache.putAll(newValues) 因为其他线程可能在刷新操作期间读取缓存。我需要某种同步 最简单的解决方案可能与以下类似: computeNewCacheValues() computeStaleKeys() //e.g. ones are in the cache but are

我想实现一个简单的缓存,它会定期更新,每次更新都会触发完全缓存清除和数据插入

伪代码:

//context calls periodically this method
cache.clear();
cache.putAll(newValues)
因为其他线程可能在刷新操作期间读取缓存。我需要某种同步

最简单的解决方案可能与以下类似:

computeNewCacheValues()
computeStaleKeys() //e.g. ones are in the cache but are not in the new cache
removeStaleKeysOneByOneFromCache()
updateKeysFromNewCacheValueOneByOne()
该实现由ConccurentHashMap实例支持-因此在缓存更新期间:

  • 没有出现并发问题(?)
  • 缓存在整个进程中未锁定(因此在刷新期间可访问)

这可能是一个很好的解决方案,但我想知道:有没有其他更有效/安全的方法来实现这一点?是否有能够执行此操作的库?

如果您总是更换整个缓存,您可以直接更换它

final AtomicReference<Map<K, V>> mapRef = new AtomicReference<>();

// assuming you don't modify the map after calling this.
public void update(Map<K, V> map) {
    mapRef.set(map);
}

public V get(K key) {
    // this will always see the latest complete map.
    return mapRef.get().get(key);
}
final AtomicReference mapRef=新的AtomicReference();
//假设调用此函数后不修改映射。
公共空间更新(地图){
mapRef.set(map);
}
公共V get(K键){
//这将始终看到最新的完整地图。
返回mapRef.get().get(键);
}
注意:无需锁定,因为地图添加到缓存后不会更改。

供将来参考:

创建“
大容量缓存”
”时,最简单的解决方案可能是@Peter提到的解决方案;在单独的线程上创建新缓存,并将旧缓存的引用更改为新缓存

需要考虑的事项:

  • 该解决方案是有效的,因为赋值运算符(“
    =
    ”)的执行是原子的
  • 缓存对象必须是易变的;这样,它的值就可以被多个线程访问(没有这个线程,一个线程可能会更改它的值,但是另一个线程将无法看到更改)
  • AtomicReference
    是另一个选项;基本上它是一个易失性对象的包装器,尽管它添加了一些有用的方法(例如用于审计)

在您的示例中,您没有用新值覆盖现有项,因此它们永远不会更改?那么,如果您可以随着时间的推移填充缓存,那么如此复杂的替换有什么意义呢?将addKeysFromNewCacheValueOneByOne更新为updateKeysFromNewCacheValueOneBeOne;谢谢那么Peter的答案很好,在调用mapRef.get()时,最好以原子方式替换整个缓存,而不是执行这些复杂的更新技巧。get(key)这是否意味着返回映射对象的副本?因此每次调用get都会执行一个复制操作?当同时调用两个get操作时会发生什么情况?@krisy每次都会获取一个引用的副本,该副本通常为4字节。不会复制贴图对象。如果多个线程同时调用此函数,则每个线程都有一个映射引用的副本,但内存中只有一个映射副本。注意:这张地图在没有锁的情况下工作,不可更改。更新缓存时,每次都必须创建一个新映射,例如HashMap。这张新地图可以基于旧地图的副本;在AtomicReference类的(J8)源代码中,我看到在后台使用了一个volatile变量。据我所知,对易失性变量的所有访问都是同步的。那么,当两个方法同时调用mapRef.get()时,这是否意味着它们将具有顺序访问权限,例如,一个线程将挂起,直到另一个线程完成?@krisy当您使用
volatile
时,不涉及锁定,您可以安全地拥有任意数量的并发读卡器。在这种情况下,您可以使用一个简单的
volatile Map
。谢谢您的回答!