前言
锁优化有一个很重要的思路,就是拆分锁的粒度,类似于分布式锁优化的实践,把一份数据拆分为多份数据,对每一份数据片段来加锁,这样就可以提升多线程并发的效率
HashMap底层不就是一个数组的数据结构么?如果你要完全保证里的并发安全,如果你每次对数组做一些put、resize、get的操作的时候,你都是加锁,synchronized好了,此时就会导致并发性能非常的低下
所有的线程读写hashmap的过程都是串行化的,hashtable,就是采用的这种做法
读写锁,大量的读锁和写锁冲突的时候,也会导致多线程并发的效率大大的降低,也不行。
ConcurrentHashMap,分段加锁,把一份数据拆分为多个segment,对每个段设置一把小锁,put操作,仅仅只是锁掉你的那个数据一个segment而已,锁一部分的数据,其他的线程操作其他segmetn的数据,跟你是没有竞争的。大大减少了锁竞争,从而提高了性能。
put()剖析
int hash = spread(key.hashCode());
对key获取了hashCode,调用了spread算法,获取到了一个hash值
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
他相当于是把hash值的高低16位都考虑到后面的hash取模算法里,这样就可以把hash值的高低16位的特征都放到hash取模算法来运算,有助于尽可能打散各个key在不同的数组的位置,降低hash冲突的概率
刚开始,table是null的话,此时就要初始化这个table。
U.compareAndSwapInt(this, SIZECTL, sc, -1):CAS操作,sizeCtl = -1
初始化一个table数组,默认的大小就是16
tabAt(tab, i = (n - 1) & hash) --》return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
i = (n - 1) & hash,这个就是hash取模的算法,定位的算法。定位出来的位置,传递给了tabAt()函数进行volatile读。