HashMap线程不安全,HashTable和ConcurrentHashMap线程安全。
Hashtable能保证线程安全,是因为在Hashtable里的关键方法里套上了synchronized(相当于给this加锁)。
只要两个线程操作同一个Hashtable就会出现锁冲突,但实际上对于哈希表来说这样相当于加了一些不必要的锁,因为有些情况是不涉及到线程安全问题的。
比如一个线程操作哈希表1号链表中的元素,另一个线程操作哈希表2号链表中的元素,在不考虑扩容的前提下,这种对哈希表的操作是不会发生线程安全问题的。如果两个线程操作哈希表的同一个链表才有可能出现线程安全问题。
①相比于Hashtable,ConcurrentHashMap最核心的改进是把Hashtable里关键方法里的大锁改进成了哈希表中每个链表独立的小锁(ConcurrentHashMap把每个链表的头节点作为锁对象),这样大幅度降低了锁冲突的概率。
②充分利用CAS特性,省去了一些不必要加锁的环节。比如需要使用变量记录哈希表中元素的个数,此时就可以使用原子操作(CAS)修改变量的值。
③ConcurrentHashMap针对读操作没有加锁。读和读之间、读和写之间不会有锁竞争,写和写之间有锁竞争。那这样ConcurrentHashMap会不会出现“读到一个修改了一半的数值”呢?答案是不会的,因为ConcurrentHashMap在底层编码的时候会比较谨慎地处理一些细节,比如修改的时候会避免使用++、–这种非原子的操作,会使用=(赋值)这种原子性的操作,这样读的时候要么读到的是写之前的旧值要么读到的是写之后的新值。
④ConcurrentHashMap针对扩容操作做出了单独的优化。哈希表在扩容的时候会把所有的元素都拷贝一遍,如果元素比较多拷贝就会比较耗时。ConcurrentHashMap在扩容时会把数据分成多次来拷贝,每次只拷贝一部分数据(化整为零)。