一个异常引发的对Hashtable和HashMap的思考
来源:互联网 发布:linux如何查看历史命令 编辑:程序博客网 时间:2024/04/26 07:30
对于Hashtable和HashMap,相信每个学习Java的人都不会陌生,这两个集合在用法上并没有什么不同,但在使用环境上却有很大差别:
(1)区别,这两个类主要有以下几方面的不同:
Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类
在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。
因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。
而在Hashtable中,无论是key还是value都不能为null。
这两个类最大的不同在于:
(1)Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。
(2)而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。
上面所提的是面试官非常热衷提问的(当然有水平的公司也会问的更深,比如HashMap的实现机制),但这并非今天要说的重点。
本人学习Java语言多年,自认对这两个类了如指掌,可是一次变成过程中所遇到的异常,让我发现自己对这两个类的理解还不够深入。
前段时间,本人使用Java语言实现了一个时间调度算法,这个算法是本人一片论文中的,该算法涉及多线程,所以我理所应当的用到了HashTable,但在测试阶段却频繁的跳出一个异常,ConcurrentModificationException,虽然很快得到了很好的解决,但这个异常,使我对HashMap,Hashtable,ConcurrentHashMap有了一个更深层的认识。
首先,很多人都知道,多线程情况下应该使用HashTable,或是使用Collections中的SynchronizedMap对HashMap进行同步。但看似同步的Map却仍然存在潜在的线程安全问题,可以考虑下面一种场景
如下面这段代码:
Java代码
<pre class="java" name="code"><span style="font-size:18px;">// shm是SynchronizedMap的一个实例 if(shm.containsKey('key')){ shm.remove(key); } </span>
这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。为什么?假设:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。这时就会抛出上面我所遇到的异常ConcurrentModificationException,要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。
我算法中所遇到的异常产生于迭代过程中,代码如下:
<span style="font-size:18px;"> Iterator keys = map.keySet().iterator(); while(keys.hasNext()){ map.get(keys.next()); } </span>
碰到这个异常的时候我也百思不解,明明是线程安全的怎么会产生这个异常,原来得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出 ConcurrentModificationException异常。
意识到问题的所在,那解决起来也容易多了,最后我使用了ConcurrentHashMap做了替换。
ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。
ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。
在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException, 取而代之的是 在改变时new新的数据从而不影响原有的数据。 iterator完成后再将头指针替换为新的数据。这样iterator线程可以使用原来老的数据。而写线程也可以并发的完成改变。
至此,问题解决,同时,对于集合的多线程又有了一个新的了解,所以以后多线程还是用ConcurrentHashMap比较好!!!
- 一个异常引发的对Hashtable和HashMap的思考
- 一个问题引发对Linux swap和内存的思考
- 一个异常引发的集合多线程思考
- 对hashMap和hashtable的理解
- Hashtable与HashMap引发的血案
- 由自定义异常引发的对 __str__ 和 repr 的思考
- 一个“java.lang.NoSuchMethodError”异常引发的思考
- 一个问题引发的对类成员指针的思考
- 从一个demo引发的对import、require的思考
- 代码干货 | 一个Reentrant Error引发的对Python信号机制的探索和思考
- 由一个小程序引发对 size_type类型的思考
- 一个“笑话”引发的思考
- 一个细节引发的思考
- 一个苹果引发的思考
- 一个问题引发的思考
- 一个帖子引发的思考
- 一个程序员引发的思考
- 一个测试引发的思考
- 分块查找
- python向数据库插入中文乱码问题
- cocosPods 遇到的问题
- 第12周项目2-操作用邻接表存储的图
- Armadillo的安装
- 一个异常引发的对Hashtable和HashMap的思考
- CEPH RGW处理请求过程
- git常用命令
- linux APT 命令
- 杭电1229 还是A+B
- Jsp+Servlet+JavaBean原生态开发问题集锦
- ios中的实现属性显示在图形界面上进行修改的知识点
- Android自定义携带Cookie的POST请求
- mysql 测试sql执行时间