4

我正在使用 LinkedHashMap 并且环境是多线程的,所以这个结构需要是线程安全的。在特定事件期间,我需要阅读整个地图推送到数据库并清除所有内容。

大多数时候,只有写发生在这张地图上。此地图有 50 个条目的限制。

我正在使用 Oracle MAF,它没有可用的 Collections.syncronizedMap。那么,我需要在同步块中放入哪些内容以确保写入和读取不会击中我 concurrentModificationException 等

几个要求:

  1. 我需要把它表现得像一个循环队列,所以覆盖 LinkedHashMap 的 removeEldestEntry 方法。
  2. 我需要保留订单
4

3 回答 3

3

那么,我需要在同步块中放入哪些内容以确保写入和读取不会击中我 concurrentModificationException 等

一切方法调用都应该在同步块中。

棘手的是使用迭代器,因为您必须在迭代器的生命周期内持有锁。例如

// pre Java 5.0 code
synchronized(map) { // the lock has to be held for the whole loop.
    for(Iterator iter = map.entrySet().iterator(); iter.hashNext(); ) {
         Map.Entry entry = iter.next();
         String key = (String) entry.getKey();
         MyType value = (MyType) entry.getValue();
         // do something with key and value.
    }
}
于 2016-02-10T17:14:31.467 回答
2

大多数LinkedHashMap操作都需要synchronization在多线程环境中,即使是那些看起来很纯粹的操作get(key)get(key)实际上也会改变一些内部节点。您可以做的最简单的事情是使用Collections.synchronizedMap.

Map<K,V> map = Collections.synchronizedMap(new LinkedHashMap<>());

现在如果它不可用,您可以轻松添加它,因为它只是一个简单的decorator环绕地图,synchronize所有操作。

class SyncMap<T,U> implements Map<T,U>{
  SyncMap<T,U>(LinkedHashMap<T,U> map){
   ..
  }
  public synchronized U get(T t){
    ..
  }
}
于 2016-02-10T17:18:54.900 回答
1

如果您使用的是 java 1.5 或更高版本,则可以使用 java.util.concurrent.ConcurrentHashMap.

这是在多线程环境中使用的最有效的 Map 实现。

它还添加了一些方法,例如putIfAbsent对地图上的原子操作非常有用。

来自java文档:

检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。对于putAll 和 clear等聚合操作,并发检索可能反映插入或删除某些条目

因此,请验证这是您对班级的期望。


如果您的地图只有 50 条记录并且需要用作循环队列,为什么要使用地图?使用 Queue 实现之一不是更好吗?


如果您需要使用 LinkedHashMap,请使用以下命令:

Map m = Collections.synchronizedMap(new LinkedHashMap());

javadocLinkedHashMap

请注意,此实现不同步。如果多个线程同时访问链接的哈希映射,并且至少有一个线程在结构上修改映射,则必须在外部同步。这通常是通过同步一些自然封装地图的对象来完成的。如果不存在这样的对象,则应使用 Collections.synchronizedMap 方法“包装”地图。这最好在创建时完成,以防止对地图的意外不同步访问:

 Map m = Collections.synchronizedMap(new LinkedHashMap(...));

https://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html

于 2016-02-10T17:11:01.863 回答