1、HashTable与HashMap

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

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

(3)HashTable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。

因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:

public static <K, V> Map<K, V> synchronizedMap(Map<K, V> m) {
return new SynchronizedMap<K, V>(m);
}

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

2、潜在的线程安全问题

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

如下面这段代码:

// shm是SynchronizedMap的一个实例
if(shm.containsKey(key)) {
shm.remove(key)
}

这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey()和remove()方法都是同步的,但是整段代码却不是。

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

要保证上面这个场景下代码按我们的医院工作,一个办法就是对这段代码进行同步控制,但是这付出的代价太大了。在进行迭代时这个问题更加明显。

Map集合共提供了三种方式来分别返回键、值、键值对的集合:

Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();

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

Iterator keys = map.keySet().iterator();  

while(keys.hasNext()) {
map.get(keys.next());
}

在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能抛出ConcurrentModificationException异常。

为了解决这个问题,通常有两种方法:

(1)一是直接返回元素的副本,而不是视图。这个可以通过集合类的toArray()方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;

(2)另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。

3、更好的选择:ConcurrentHashMap

Java 5中新增了ConcurrentMap接口和它的实现类ConcurrentHashMap。

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

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

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

HashMap、HashTable与ConcurrentHashMap的区别的更多相关文章

  1. HashMap HashTable和ConcurrentHashMap的区别

    HashMap和Hashtable都实现了Map接口,其主要的区别有:线程安全性,同步(synchronization),以及效率. HashMap和Hashtable基本上没啥区别,除了HashMa ...

  2. 集合 HashMap 的原理,与 Hashtable、ConcurrentHashMap 的区别

    一.HashMap 的原理 1.HashMap简介 简单来讲,HashMap底层是由数组+链表的形式实现,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表 ...

  3. Java Main Differences between HashMap HashTable and ConcurrentHashMap

    转自这篇帖子:http://www.importnew.com/7010.html HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以 ...

  4. HashMap和Hashtable以及ConcurrentHashMap的区别

    ​ HashMap和Hashtable的区别 何为HashMap HashMap是在JDK1.2中引入的Map的实现类. HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部 ...

  5. HashMap、HashTable、ConcurrentHashMap的区别

    一.相关概念 1.Map的概念 javadoc中对Map的解释如下: An objectthat maps keys to values . Amap cannot contain duplicate ...

  6. HashMap、Hashtable和ConcurrentHashMap的区别

    HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相 ...

  7. HashMap,HashTable ,LinkedHashMap,TreeMap的区别

    Map:主要是存储键值对,不允许键重复,但可以值重复. HashMap:根据键的HashCode值来存储数据,根据键直接获取值.具有很快的访问速度,遍历时,取得的数据值的顺序都是随机的.hashMap ...

  8. HashTable与ConcurrentHashMap的区别

  9. HashMap、HashTable、ConcurrentHashMap、HashSet区别 线程安全类

    HashMap专题:HashMap的实现原理--链表散列 HashTable专题:Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析 Hash,Tree数据结构时 ...

随机推荐

  1. SpringBoot 过滤器, 拦截器, 监听器 对比及使用场景

    1. 过滤器 (实现 javax.servlet.Filter 接口) ① 过滤器是在web应用启动的时候初始化一次, 在web应用停止的时候销毁. ② 可以对请求的URL进行过滤, 对敏感词过滤, ...

  2. 什么叫pure function(纯函数)

    (来自:http://en.wikipedia.org/wiki/Pure_function) 在计算机编程中,假如满足下面这两个句子的约束,一个函数可能被描述为一个纯函数: 给出同样的参数值,该函数 ...

  3. Performance --- 前端性能监控

    阅读目录 一:什么是Performance? 二:使用 performance.timing 来计算值 三:前端性能如何优化? 四:Performance中方法 五:使用performane编写小工具 ...

  4. docker 清理无用的卷

    docker  system prune 对于卷的清理不够彻底 题外话:docker volume ls查看当前卷列表 使用如下命令可以清理不用的卷 docker volume prune -f 强制 ...

  5. Wbbpack --3配置

    Wbbpack --3配置 什么是webpack webpack 是一个现代 JavaScript 应用程序的静态模块打包(modulebundler)当 webpack 处理应用程序时,它会递归地构 ...

  6. leetcode 数据库练习 - 1205 每月交易I和II

    每月交易(一) Table: Transactions +---------------+---------+| Column Name | Type |+---------------+------ ...

  7. [RN] React Native 删除第三方开源组件依赖包 后 还要做的 (以 删除 react-native-video为例)

    近期测试使用了下  react-native-video 使用一直不成功,后来想着删除掉, 使用命令: npm uninstall react-native-video 重新编译后,还是一直报错 后来 ...

  8. linux 运维基本操作

    本记录来自腾讯云实验  https://cloud.tencent.com/developer/labs/lab/10000 目录操作 任务时间:5min ~ 10min 创建目录 使用 mkdir ...

  9. 【cf比赛记录】Codeforces Round #600 (Div. 2)

    Codeforces Round #600 (Div. 2) ---- 比赛传送门 昨晚成绩还好,AC A,B题,还能上分(到底有多菜) 补了C.D题,因为昨晚对C.D题已经有想法了,所以补起题来也快 ...

  10. 分析WordPress数据表之评论表(功能篇)

    数据表分析 wp_comments(评论表) 该表字段,如下:comment_ID(评论ID)comment_post_ID(评论文章ID)comment_author(评论者用户名)comment_ ...