Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/364.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 多线程使用'ConcurrentHashMap'迭代器_Java_Caching_Concurrency_Guava_Concurrenthashmap - Fatal编程技术网

Java 多线程使用'ConcurrentHashMap'迭代器

Java 多线程使用'ConcurrentHashMap'迭代器,java,caching,concurrency,guava,concurrenthashmap,Java,Caching,Concurrency,Guava,Concurrenthashmap,我需要编写一个缓存的特定实现,它有唯一的键,但可以包含重复的值,例如: "/path/to/one" -> 1 "/path/to/two" -> 2 "/path/to/vienas" -> 1 "/path/to/du" -> 2 该类需要提供非阻塞读取/键查找,但也有典型的创建/更新/删除变异。例如,删除值2将导致 "/path/to/one" -> 1 "/path/to/vienas" -> 1 到目前为止,此缓存的读操作将超过写操作,因

我需要编写一个缓存的特定实现,它有唯一的键,但可以包含重复的值,例如:

 "/path/to/one" -> 1
 "/path/to/two" -> 2
 "/path/to/vienas" -> 1
 "/path/to/du" -> 2
该类需要提供非阻塞读取/键查找,但也有典型的创建/更新/删除变异。例如,删除值
2
将导致

"/path/to/one" -> 1
"/path/to/vienas" -> 1
到目前为止,此缓存的读操作将超过写操作,因此写操作性能不是一个问题-只要并发写操作不相互重叠运行即可。条目的总数很可能会少于1000,因此偶尔对值进行迭代仍然是可以承受的

所以我写了这样的东西(伪代码):

因此,如果我用
最终ConcurrentHashMap
替换
volatile ImmutableMap
的用法,并删除所有
synchronized
块,那么相互竞争的并发突变是否可能会使彼此失效?例如,我可以想象对
remove
的两个并发调用如何导致竞争条件,从而使第一个
remove
的结果完全无效

我所能看到的唯一改进是,通过使用
最终ConcurrentHashMap
和保持
同步
,我至少可以避免不必要的数据复制


这有意义吗?或者也许我在这里忽略了什么?有人能为这个解决方案推荐其他替代方案吗?

如果您进行了替换,您仍然一次只有一个线程使用给定的迭代器。 该警告意味着两个线程不应使用同一迭代器实例。并不是说两个线程不能同时迭代

您可能遇到的问题是,由于删除操作不能在ConcurrentMap的单个原子操作中完成,因此您可以让一个并发线程以中间状态查看映射:一个值已删除,但另一个值未删除


我不确定这是否会更快,因为您说写性能不是问题,但要避免每次写时都复制映射,可以做的是使用ReadWriteLock保护可变的ConcurrentMap。所有读取仍然是并发的,但是对映射的写入将阻止所有其他线程访问映射。您不必在每次修改时都创建一个新副本。

可以同时通过多个迭代器/多个线程对
ConcurrentHashMap
进行变异。只是您不应该将迭代器的单个实例交给多个线程并同时使用它

因此,如果使用
ConcurrentHashMap
,则不需要将
synchronized
留在那里。正如JB Nizet所指出的,这与当前实现之间的区别在于中间状态的可见性。如果您不关心这一点,那么使用
ConcurrentHashMap
将是我的首选,因为实现最简单,您不必担心读写性能

//
// tl;dr all writes are synchronized on a single lock and each
// resets the reference to the volatile immutable map after finishing
//
class CopyOnWriteCache {
   private volatile Map<K, V> readOnlyMap = ImmutableMap.of();

   private final Object writeLock = new Object();

   public void add(CacheEntry entry) {
      synchronized (writeLock) {
         readOnlyMap = new ImmutableMap.Builder<K, V>()
            .addAll(readOnlyMap)
            .add(entry.key, entry.value)
            .build();
      }
   }

   public void remove(CacheEntry entry) {
      synchronized (writeLock) {
         Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry));
         readOnlyMap = ImmutableMap.copyOf(filtered);
      }
   }

   public void update(CacheEntry entry) {
      synchronized (writeLock) {
         Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry));
         readOnlyMap = new ImmutableMap.Builder<K, V>()
             .addAll(filtered)
             .add(entry.key, entry.value)
             .build();
      }
   }

   public SomeValue lookup(K key) {
      return readOnlyMap.get(key);
   }
}
iterators are designed to be used by only one thread at a time