如何让HashMap线程安全
HashMap扩容相关问题
1. 什么时候扩容?每次扩容多少?
负载因子0.75,是实际存储的元素个数和数组容量的比值。达到75%则进行扩容,一次扩容是2的整数倍,每次都往上乘2,2的几次几次幂这样的。
2. 为什么和2的几次幂来扩容?
这和降低Hash冲突有关。
如何让HashMap线程安全
1. 使用synchronziedMap
通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的。这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map的实现。
1.1 特点
通过Collections.synchronizedMap()来封装所有不安全的HashMap的方法,就连
toString,hashCode都进行了封装。封装的关键点有2处:
- 使用了经典的synchronized来进行互斥
- 使用了代理模式new了一个新的类,这个类同样实现了Map接口。
在Hashmap上面,synchronized锁住的是对象,所以第一个申请的得到锁,其他线程将进入阻
塞,等待唤醒。
1.2 优点
代码实现十分简单,一看就懂
1.3 缺点
从锁的角度来看,方法一直接使用了锁住方法,基本上是锁住了尽可能大的代码块。性能会比较差。
2. 使用ConcurrentHashMap
重新改写HashMap,具体可以查看java.util.concurrent.ConcurrentHashMap。
2.1 特点
重新写了HashMap,比较大的改变有如下几点:
使用了新的锁机制,把HashMap进行了拆分,拆分成了多个独立的块,这样在高并发的情况下减少了锁冲突的可能,使用的是NonfairSync。这个特性调用CAS指令来确保原子性与互斥性。当如果多个线程恰好操作到同一个segment上面,那么只会有一个线程得到运行。
2.2 优点
需要互斥的代码段比较少,性能会比较好。ConcurrentHashMap把整个Map切分成了多个块,发生锁碰撞的几率大大降低,性能会比较好。
2.3 缺点
代码繁琐,这里指的是源码的繁琐。