Collections类为HashMap提供了一个并发版本SynchronizedMap类. 虽然SynchronizedMap类中的方法都使用了synchronized修饰,但并不等于是线程安全的.

Map<<tring, Object> map = Maps.newHashMap();
Map<String, Object> synMap = Collections.synchronizedMap(map);

String key = "phone";
if(synMap.containsKey(key)){
    synMap.remove(key);
}

上面代码用于从map中删除一个元素之前判断是否存在这个元素. 虽然containsKey和remove方法都是被synchronized修饰,但整段代码却不是线程安全的.

考虑这么一个使用场景: 线程A执行了containsKey方法返回true,准备执行remove操作;这时线程B开始执行,同样 执行了containsKey方法返回true,并接着执行remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了.

要保证这段代码线程安全,一个办法就是对这段代码进行同步控制,但是代价太大.

迭代操作时,Map集合提供了三种方式分别返回键,值,键值对的结合.
Set keySet()
Collection values()
Set<Map.Entry<K,V>> entrySet()
在这三个方法的基础上,一般通过如下方式访问Map的元素

Iterator keys = map.keySet().iterator();
while(keys.hasNext()) {
	map.get(keys.next());
}

上面代码中keySet和迭代器都是Map中元素的一个’视图’,而不是’副本’.
当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素.此时,在迭代元素时就可能会抛出ConcurrentModificationException异常.

为了解决这个问题通常有两种方法: 一是直接返回元素的副本,而不是视图,这个可以通过集合类的toArray()方法实现, 但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下; 另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了.

Java 5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap.
ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制.
Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶.
ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶.这样原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的. 而且这16个线程指的是写线程,而读操作大部分时候都不需要用到锁,只有在size等操作时才需要锁住整个hash表.


 
SynchronizedMap和ConcurrentHashMap深入分析_其他