提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一.hashmap、concurrenthashmap和hashtable线程安全性
hashmap是线程不安全的,concurrenthashmap和hashtable都是线程安全性的。写个程序验证一下。
创建两个线程,两个线程各往同一容器中写入十万条数据,且key不存在重复。可以看到全部20万条数据成功落入hashtable和courrentHashMap,由此可见hashtable和courrentHashMap是线程安全的。而hashmap少了近三千条数据,故hashmap线程不安全。
二、hashmap为什么线程不安全?
hashmap中未引入任何锁机制,既没有使用synchronized关键字,也没有创建ReentrantLock类对象。那硬使用多线程操作hashmap会有什么问题。我们看看源码(hashmap源码在另篇博客HashMap底层原理及hashmap扩容中进行了详细解读)。
如果A线程先运行到629行,发现数组第5格是空,于是准备运行630行,即往数组第五格插入元素。但不巧这时候线程A挂起了,线程B开始运行,线程B也运行到629行,又不巧线程B的key和线程A的key存在hash冲突。于是线程B也发现第五格是空,并成功运行了第630行,即往数组第五格插入元素。然后线程B开始挂起,线程A开始运行。由于线程A已经判定第五格没有元素,而它也并不知道线程B已经往第五格中插入元素了。于是,线程A会继续运行第630行在数组第五格插入元素。结果就是线程B插入的元素就被线程A覆盖掉了。641行采用链式寻址法处理hash冲突也会遇到一样的问题。
三、hashtable多线程和courrenthashmap多线程效率对比
1.先比较单线程情况下两者效率
可以看到,单线程环境下写入一百万条数据hashtable是要远快于concurrentHashMap的。
2.再比较多线程情况下两者效率
创建十条线程,每个都往同一容器中写入十万条数据,比较hashtable和concurrenthashmap的速度。
可以看到,多线程下同样写一百万条数据,hashtable的速度比起单线程反而慢了近一倍。
而同样是多线程写一百万条数据,ConcurrentHashmap的速度相比单线程快了近三倍。
四、为什么hashtable多线程效率低
进hashtable源码可以发现hashtable实现多线程的方式非常简单粗暴,就是在每个方法都加上了synchronized关键字,synchronized关键字修饰非静态方法就意味着当一个线程访问了这个方法,其他线程都不能访问该方法。一核工作,九核围观。这样不仅提升不了多线程下程序运行速度,还会因为线程的切入切出导致资源的浪费,这就是为什么hashtable多线程下速度反而比单线程慢了。
五、为什么concurrenthashmap多线程效率高
可以看到concurrenthashmap对于锁的应用精准多了。以put方法举例,1014行用到了自旋锁,1019行用到了cas乐观锁,1027行用synchronized关键字修饰局部变量实现了分段锁。
1018行表示计算完hash值后最终确认的数组位置如果是空,进入1019行。
1019行代码表示给该数组位置插入元素前,判断一下是否有别的线程也在这个位置插入了数据,没有则插入节点跳出自旋,反之则不插入元素,进行自旋操作。
1025行之后均是处理hash冲突,其操作与hashmap类似,不同的是程序对于传入节点位于数组上的位置进行了上锁,也就是对桶上锁,使得同一时刻别的线程无法再操作这个数组位置。但数组的其他位置是能操作的。实现了分段锁。
六、总结
hashmap不支持多线程。hashtable虽然支持多线程但本身使用的重量级锁效率太低。而concurrenthashmap使用了乐观锁、自旋锁和分段锁,使得程序的整体效率大幅提高。