没有 Collections.synchronized 的线程安全 LinkedHashMap

thread safe LinkedHashMap without Collections.synchronized

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

大多数时候只有写入发生在这张地图上。此地图最多可包含 50 个条目。

我正在使用 Oracle MAF,它没有 Collections.syncronizedMap 可用。那么,我需要在同步块中放入什么东西来确保写入和读取不会影响我的 concurrentModificationException 等等

要求不高:

  1. 我需要让它像循环队列一样运行,所以重写 LinkedHashMap 的 removeEldestEntry 方法。
  2. 我需要保留顺序

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

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

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

来自 java 文档:

Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset. For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries

因此请验证这是否是您期望 class 的行为。


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


如果您需要使用 LinkedHashMap,请使用以下内容:

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

来自 LinkedHashMapjavadoc

Note that this implementation is not synchronized. If multiple threads access a linked hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:

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

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

So, what are things I need to put in synchronized blocks to make sure writing and reading doesn't hit me concurrentModificationException etc

所有方法调用都应该在同步块中。

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

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

大多数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){
    ..
  }
}