转自:http://blog.sina.com.cn/s/blog_5157093c0100hm3y.html

在开始之前,先介绍下Map是什么?

javadoc中对Map的解释如下:

An object that maps keys to values . A map cannot contain duplicate keys; each key can map to at most one value.

This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.

The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.

从上可知,Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。

Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。

1. Hashtable 和 HashMap

这两个类主要有以下几方面的不同:

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。

在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null 。

这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:

Java代码
  1. public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
  2. return new SynchronizedMap<K,V>(m);
  3. }

该方法返回的是一个SynchronizedMap 的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized 关键字进行了同步控制。

2. 潜在的线程安全问题

上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。

如下面这段代码:

Java代码
  1. // shm是SynchronizedMap的一个实例
  2. if(shm.containsKey('key')){
  3. shm.remove(key);
  4. }

这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么一个使用场景:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。

在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:

Java代码
  1. Set<K> keySet();
  2. Collection<V> values();
  3. Set<Map.Entry<K,V>> entrySet();

在这三个方法的基础上,我们一般通过如下方式访问Map的元素:

Java代码
  1. Iterator keys = map.keySet().iterator();
  2. while(keys.hasNext()){
  3. map.get(keys.next());
  4. }

在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本” 。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出 ConcurrentModificationException异常。为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过

集合类的 toArray() 方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。


3. 更好的选择:ConcurrentHashMap

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

上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。

在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。

SynchronizedMap和ConcurrentHashMap的深入分析相关推荐

  1. SynchronizedMap和ConcurrentHashMap 区别

    SynchronizedMap和ConcurrentHashMap的深入分析 在开始之前,先介绍下Map是什么? javadoc中对Map的解释如下: An objectthat maps keys ...

  2. SynchronizedMap和ConcurrentHashMap有什么区别

    SynchronizedMap实现上在调用Map的所有方法是,对整个map进行了同步! public V put(K key, V value) {synchronized (mutex) {retu ...

  3. ConcurrentHashMap和Collections.synchronizedMap(Map)的区别是什么?

    ConcurrentHashMap和Collections.synchronizedMap(Map)的区别是什么? 我有一个会被多个线程同时修改的Map 在Java的API里面,有3种不同的实现了同步 ...

  4. ConcurrentHashMap 和 Collections.synchronizedMap(map) 比较

    ConcurrentHashMap 和 Collections.synchronizedMap(map) 比较 如果你有一个 Map 将会被几个线程同时修改, 那么在Java API 中 你有三种不同 ...

  5. ConcurrentHashMap总结

    为什么80%的码农都做不了架构师?>>>    并发编程实践中,ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Collections.sy ...

  6. 解读Java8中ConcurrentHashMap是如何保证线程安全的

    HashMap是工作中使用频度非常高的一个K-V存储容器.在多线程环境下,使用HashMap是不安全的,可能产生各种非期望的结果. 关于HashMap线程安全问题,可参考笔者的另一篇文章: 深入解读H ...

  7. android ConcurrentHashMap的使用

    今天,简单讲讲ConcurrentHashMap使用. 一.ConcurrentHashMap类的理解 ConcurrentHashMap是Java 中支持高并发,高吞吐量的hashMap实现.Con ...

  8. Java 7:HashMap与ConcurrentHashMap

    从我过去有关性能的文章和HashMap案例研究中可能已经看到,Java线程安全性问题可以很轻松地使Java EE应用程序和Java EE容器崩溃. 在对Java EE性能问题进行故障排除时,我观察到的 ...

  9. ConcurrentHashMap 总结( 上 )

    来源:Hosee, my.oschina.net/hosee/blog/675884 并发编程实践中,ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Coll ...

最新文章

  1. 强哥原创管理方法论之“掌纹管理学”
  2. Netbeans配置Java SE嵌入式平台(树莓派)
  3. rabbit-mq cluster安装
  4. python3 域名转ip
  5. windows下查看dns缓存和刷新缓存
  6. 【Linux 内核】调度器 ③ ( sched_class 调度类结构体分析 | next 字段 | enqueue_task 函数 | dequeue_task 函数 )
  7. linux日志绕接,[判断题] 绕接式保安接线排按结构分为固定式和旋转式。
  8. 终止中台乱象 《2021年中国中台市场研究报告》隆重发布
  9. 百度统计 java 实现思路_211本+985硕+计算机专业投面百度,坐等一周迎来三面,已拿offer...
  10. goland配置mysql失败_GoLand配置数据库、远程host以及远程调试
  11. Java 从入门到精通 第16章String类
  12. 广东高等学校计算机水平考试准考证打印,广东高考准考证打印系统
  13. 时间序列(数据分析)
  14. 微信小程序申请微信支付0.2费率商户号微信小程序接入开通流程
  15. 华东师范大学副校长周傲英:未来,中国需要什么样的数据库?
  16. 我的2011,一半是海水,一半是烈焰
  17. 电影后台管理系统(实训)
  18. 使用CStdioFile操作文件
  19. 请记住内核中这个勤劳的监测卫士---Watchdog(Soft lockup篇)
  20. 少说话多写代码之Python学习062——标准模块(random模块)

热门文章

  1. OSChina 周一乱弹 —— 抱着漂亮袜子就亲了一口
  2. 【跃迁之路】【597天】程序员高效学习方法论探索系列(实验阶段354-2018.09.25)...
  3. 烧流量还是打矩阵,短视频不疯魔不成活?
  4. MariaDB三种方法安装及多实例实现
  5. 第六十六篇、OC_Sqlite数据库操作
  6. 贪心,POJ(2709)
  7. 栈(Stack) 任何程序执行前,预先分配一固定长度的内存空间
  8. linux之vsftpd虚拟用户搭建
  9. 打开 XP Pro SP2 远程桌面的多用户支持
  10. 用一句sql语句更新两个表并可更新对应的字段的值