Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在刷新期间锁定哈希映射?_Java_Concurrency - Fatal编程技术网

Java 如何在刷新期间锁定哈希映射?

Java 如何在刷新期间锁定哈希映射?,java,concurrency,Java,Concurrency,我有一个静态的HashMap,它在应用程序启动时填充,并每天刷新 如何确保刷新期间没有其他线程可以访问映射 @ThreadSafe public class MyService { private static final Map<String, Object> map = new HashMap<>(); private MyDao dao; public void refresh(List<Object> objects) {

我有一个静态的
HashMap
,它在应用程序启动时填充,并每天刷新

如何确保刷新期间没有其他线程可以访问映射

@ThreadSafe
public class MyService {

   private static final Map<String, Object> map = new HashMap<>();
   private MyDao dao;

   public void refresh(List<Object> objects) {
       map.clear();
       map.addAll(dao.findAll()); //maybe long running routine
   }

   public Object get(String key) {
       map.get(key); //ensure this waits during a refresh??
   }
}
@ThreadSafe
公共类MyService{
私有静态最终映射=新HashMap();
私人密道;
公共无效刷新(列出对象){
map.clear();
map.addAll(dao.findAll());//可能是长时间运行的例程
}
公共对象获取(字符串键){
map.get(key);//确保在刷新期间等待此操作??
}
}

我是否应该引入一个简单的
布尔锁
,在
刷新()过程中设置并清除该锁
?还是有更好的选择?或者
同步
机制是一种可行的方法吗?

在这里,使用同步块或读写锁是更好的选择。这样,您就不必更改调用代码中的任何内容


您也可以使用concurrentHash,但在这种情况下,对于聚合操作(如putAll和clear),并发检索可能只反映插入或删除某些条目。

您可以使用volatile映射,并在填充后重新分配它:

public class MyService {

   private static volatile Map<String, Object> map = new HashMap<>();
   private MyDao dao;

   public void refresh(List<Object> objects) {
       Map<String, Object> newMap = new HashMap<>();
       newMap.addAll(dao.findAll()); //maybe long running routine
       map = newMap;
   }

   public Object get(String key) {
       map.get(key); //ensure this waits during a refresh??
   }
}
公共类MyService{
私有静态volatile Map=newhashmap();
私人密道;
公共无效刷新(列出对象){
Map newMap=newhashmap();
newMap.addAll(dao.findAll());//可能是长时间运行的例程
map=newMap;
}
公共对象获取(字符串键){
map.get(key);//确保在刷新期间等待此操作??
}
}
它是非阻塞的,从
newMap
map
的分配是原子的,并确保可见性:任何后续调用
get
都将基于刷新的映射

就性能而言,这应该可以很好地工作,因为易失性读取几乎与正常读取一样快。Volatile写入速度稍微慢一点,但考虑到刷新频率,这不应该是一个问题。如果性能很重要,您应该运行适当的测试


注意:您必须确保没有外部代码可以访问
map
引用,否则该代码可能会访问过时的数据。

对于这样一个全局映射,您需要
clear()
然后
addAll()
,这很奇怪。我发现您的问题需要通过
ReadWriteLock
受保护的双缓冲正确解决


无论如何,从纯性能的角度来看,在CPU内核总数小于32且读写比写多得多的普通服务器上,
ConcurrentHashMap
可能是您的最佳选择。否则需要逐个研究。

请不要将map属性设置为静态,所有访问器方法都是非静态的

如果
get
应该等待或
refresh
改变映射而不是完全交换它,那么ReadWriteLock就是一种方法。ConcurrentMap如果集合发生了变异,但
get
不应等待

但是如果
refresh
完全取代了map,我可能会建议不同的非等待实现:

1) 在同步块外执行长时间运行操作

public void refresh() {
       Map<String, Object> objs = dao.findAll();
       synchronized(this) {
         map.clear();
         map.addAll(objs); 
       }
}

public Object get(String key) {
    synchronized(this) {
       return map.get(key); 
    }
}
public void refresh(){
Map objs=dao.findAll();
已同步(此){
map.clear();
map.addAll(objs);
}
}
公共对象获取(字符串键){
已同步(此){
返回map.get(key);
}
}
读卡器不是并行运行的,而是完全有效的

2) 使用非更改集合的易失性非最终引用:

// guava's ImmutableHashMap instead of Map would be even better
private volatile Map<String, Object> map = new HashMap<>();

public void refresh() {
    Map<String, Object> map = dao.findAll();
    this.map = map;
}
//番石榴的ImmutableHashMap代替Map会更好
private volatile Map=new HashMap();
公共无效刷新(){
Map=dao.findAll();
this.map=map;
}
3) 非更改集合的原子引用

也可以使用原子引用代替易失性引用。可能更好,因为它比容易漏掉的volatile更明确

// guava's ImmutableHashMap instead of Map would be even better
private final AtomicReference<Map<String, Object>> mapRef = 
    new AtomicReference<>(new HashMap<String, Object>());

public void refresh() {
    mapRef.set(dao.findAll());
}

public Object get(String key) {
    return map.get().get(key); 
}
//番石榴的ImmutableHashMap代替Map会更好
专用最终原子参考mapRef=
新的原子引用(新的HashMap());
公共无效刷新(){
mapRef.set(dao.findAll());
}
公共对象获取(字符串键){
返回map.get().get(键);
}

我认为布尔锁是一种方法。为什么不使用concurrentHashMap?使用锁是一种简单的方法,而且可以工作。如果你想做更多的设计,我认为信号灯原则将是一个不错的选择。并发hashmap会确保clear+addAll之间不可能有
get()
?我不这么认为。这确实是一个很好的解决方案,我们会这样做。这是有效的,因为我们假设只有一个线程在写,对吗?否则,我可能会丢失更新。@atamanroman,因为刷新调用问题代码中的清除,这是预期的行为。@atamanroman无论线程数多少,它都会工作-调用
get
的线程将始终看到映射的最新(完整)更新。