假设有以下场景:
某一个业务要求,定时的从数据库中取出一批数据放入一个hashmap中。
有多个线程对这个MAP进行读操作。
每过一段时间(如3分钟),都会对这个MAP进行修改(put or remove)
该场景对数据的一致性没有非常严格的要求。
如何能够在不使用同步的情况下,对这个MAP进行安全的操作呢?(例如不用concurrentHashMap,或Collections.synchronizedMap)
我的想法是,每次在需要修改MAP的时候,可以创建一个旧MAP的副本,对副本进行修改后,再替换原先的MAP
例如:
Map newMap = oldMap.clone(); //或者用new HashMap().putall(oldMap)来创造副本
newMap.put / remove ..... //修改map
oldMap = newMap; //替换上去
这样就不会因为对同一个map的并发读写而导致快速迭代失败。有没有大神看看这样有什么潜在的问题,比如GC。。请不吝赐教>_<
(
jdk 1.6 api hashMap
在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。
)
创建一个旧MAP的副本,对副本进行修改后,再替换原先的MAP
这样做还是可能出现问题的,数据还是有可能出错,同步才是最安全的办法。
尽量用同步,可以避免很多的问题,同步:
synchronizedMap
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
返回由指定映射支持的同步(线程安全的)映射。为了保证按顺序访问,必须通过返回的映射完成 所有对底层实现映射的访问。
在返回映射的任意 collection 视图上进行迭代时,用户必须手工在返回的映射上进行同步:
Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // Needn't be in synchronized block
...
synchronized(m) { // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
不遵从此建议将导致无法确定的行为。
如果指定映射是可序列化的,则返回的映射也将是可序列化的。
参数:
m - 被“包装”在同步映射中的映射。
返回:
指定映射的同步视图。
除了内存使用量会增加,我认为没问题。
不知道是何时如何取得新MAP的新迭代器,其他不好判断。