Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。
hashmap( 效率高于hashTable 、非线程安全 )
如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
·底层数组+链表实现,可以存储null键和null值
hashtable( 线程安全、效率低下
)
HashTable 只有一把锁,当一个线程访问HashTable的同步方法时,会将整张table 锁住,当其他线程也想访问HashTable 同步方法时,就会进入阻塞或轮询状态。也就是确保同一时间只有一个线程对同步方法的占用,避免多个线程同时对数据的修改,确保线程的安全性。但HashTable 对get,put,remove 方法都使用了同步操作,这就造成如果两个线程都只想使用get 方法去读取数据时,因为一个线程先到进行了锁操作,另一个线程就不得不等待,这样必然导致效率低下,而且竞争越激烈,效率越低下。
· 底层数组+链表实现,无论key还是value都不能为null
ConcurrentHashMap(加入了分段锁,并发又安全)
在hashMap 的基础上,ConcurrentHashMap 将数据分为多个segment(默认16个),然后每次操作对一个segment 加锁,HashTable 在竞争激烈的并发环境下表现出效率低下的原因是,由于所有访问HashTable的线程都必须竞争同一把锁,而ConcurrentHashMap 将数据分到多个segment 中(默认16,此时效率提升16倍),每个segment 都有一个自己的锁,只要多个线程访问的不是同一个segment 就没有锁争用,就没有堵塞,也就是允许16个线程并发的更新而尽量没有锁争用。
·底层采用分段的数组+链表实现
弱一致性的体现: get 操作是弱一致、 clear 方法是弱一致的 迭代器弱一致的
1.ConcurrentHashMap#get():为 get 操作几乎所有时候都是一个无锁操作,使得同一个
Segment 实例上的 put 和 get 可以同时进行,这就是 get 操作是弱一致的根本原因。
2.ConcurrentHashMap#clear():没有全局的锁,在清除完一个 segment 之后,正在清理下一个segment 的时候,已经清理的 segment 可能又被加入了数据,因此 clear返回的时候ConcurrentHashMap 中是可能存在数据的。因此,clear 方法是弱一致的。
为何多 个 线 程 并 发 修 改 ConcurrentHashMap 时 不 会 ConcurrentModificationException
在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛ConcurrentModificationException 异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是 ConcurrentHashMap 迭代器弱一致的表现。
在这种迭代方式中,当 iterator 被创建后,集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时 new 新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样iterator 线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。