ConcurrentHashMap

ConcurrentHashMap
1.实现原理
2.数据结构
3.增删改查

一.实现原理
锁分离   
ConcurrentHashMap使用分段锁技术,将数据分段存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,
其他段的数据也能被其他线程访问,能够实现真正的并发访问。

对于一个key,需要经过三次(为什么要hash三次下文会详细讲解)hash操作,才能最终定位这个元素的位置,这三次hash分别为:

对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key);
将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素的放在哪个Segment;
将得到的h1进行第三次hash,得到hash值h3,也即h3 = hash3(h1),通过h3能够确定该元素放置在哪个HashEntry。

二.数据结构

public class ConcurrentHashMap<K, V> {
    final int segmentMask; // 段掩码 等于数组长度-1 默认为15
    final int segmentShift; // 段偏移量 等于 32 - 数组长度从1向左移位的次数, 长度默认为16所以默认为32-4=28
    final Segment<K,V>[] segments;
}

static final class Segment<K,V> extends ReentrantLock{
    transient volatile int count; // 统计该段数据的个数
    transient int modCount; // 统计段结构改变的次数
    transient int threshold; // 表示需要进行rehash的界限值
    transient volatile HashEntry<K,V>[] table;
    final float loadFactor; // 表示负载因子
}

static final class HashEntry<K,V> {
    final K key;
    final int hash;
    volatile V value;
    final HashEntry<K,V> next;
}


除了value不是final的,其它值都是final的,因此所有的节点的修改只能从头部开始

三.增删改查
remove
1.定位到segment
2.加锁
3.定位到要删除的数据

4.将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。

	public V remove(Object key) {
	<span style="white-space:pre">	</span>int hash = hash(key.hashCode());
	<span style="white-space:pre">	</span>return segmentFor(hash).remove(key, hash, null);
	}
        V remove(Object key, int hash, Object value) {
            lock();
            try {
                int c = count - 1;
                HashEntry<K,V>[] tab = table;
                int index = hash & (tab.length - 1);
                HashEntry<K,V> first = tab[index];
                HashEntry<K,V> e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                V oldValue = null;
                if (e != null) {
                    V v = e.value;
                    if (value == null || value.equals(v)) {
                        oldValue = v;
                        // All entries following removed node can stay
                        // in list, but all preceding ones need to be
                        // cloned.
                        ++modCount;
                        HashEntry<K,V> newFirst = e.next;
                        for (HashEntry<K,V> p = first; p != e; p = p.next)
                            newFirst = new HashEntry<K,V>(p.key, p.hash,
                                                          newFirst, p.value);
                        tab[index] = newFirst;
                        count = c; // write-volatile
                    }
                }
                return oldValue;
            } finally {
                unlock();
            }
        }



put
1.通过hash算法得到对应的分段
2.向分段中push值
3.判断是否需要rehash
4.修改modcount
5.插入值,一律添加到Hash链的头部。
6.修改count



get
1.定位到segment
2.阅读count,保证happens_before(不是绝对安全)
3.获得值
4.如果为空则加锁重新读

size
1.先给3次机会,不lock所有的Segment,遍历所有Segment,累加各个Segment的大小得到整个Map的大小,
如果某相邻的两次计算获取的所有Segment的更新的次数(每个Segment都有一个modCount变量,
这个变量在Segment中的Entry被修改时会加一,通过这个值可以得到每个Segment的更新操作的次数)是一样的,
说明计算过程中没有更新操作,则直接返回这个值。

2.如果这三次不加锁的计算过程中Map的更新次数有变化,则之后的计算先对所有的Segment加锁,再遍历所有Segment计算Map大小,最后再解锁所有Segment
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值