Map集合--线程安全的ConcurrentHashMap(JDK1.7)

一、ConcurrentHashMap的数据结构

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>  
        implements ConcurrentMap<K, V>, Serializable {  
    final int segmentMask;           //用于确定key所在的segment,key的hash值被用来选择segment

    final int segmentShift;  

    final Segment<K,V>[] segments;   //一个HashMap中包含多个segments,每个segments都是一个HashTable
}  

其中segmentMask、和segmentShift用于定位key所在的Segment。

static final class Segment<K,V> extends ReentrantLock implements Serializable {  
        private static final long serialVersionUID = 2249069246763182397L;          

        transient volatile int count;              //当前Segment中元素的数量   

        transient int modCount;                    //??           

        transient int threshold;                   //当count的值超过threshold之后就rehash   

        transient volatile HashEntry<K,V>[] table; //   

        final float loadFactor;                    //负载因子  
}  

从上面中我们可以看到Segment继承了ReentranLock,也就是说我们每一段都加了一个锁,因此如果有一个线程需要向Map中添加Element的时候,其他线程就不能访问该Segment了,这样就改善了HashTable锁住整张表的性能,同时保证了线程安全。

二、Hash方法和定位段

第一步、给定一个key,计算器hash值的方法。

private static int hash(int h) {  
    // Spread bits to regularize both segment and index locations,  
    // using variant of single-word Wang/Jenkins hash.  
    h += (h <<  15) ^ 0xffffcd7d;  
    h ^= (h >>> 10);  
    h += (h <<   3);  
    h ^= (h >>>  6);  
    h += (h <<   2) + (h << 14);  
    return h ^ (h >>> 16);  
}  

第二步、segmentFor方法用于确定key映射到那个Segment中

final Segment<K,V> segmentFor(int hash) {  
    return segments[(hash >>> segmentShift) & segmentMask];  
} 

第三步、然后根据每个Segment中的table.length确定key所在的table的index

int index = hash&(table.length-1);

所以确定一个key的位置需要经历以上三个步骤

三、put方法

V put(K key, int hash, V value, boolean onlyIfAbsent) {  
    lock(); //1、对所有访问这个段的put方法的线程加锁
    try {  
        int c = count;  
        if (c++ > threshold) //2、如果Segment中的HashEntry的数量超过了threshold,那么执行rehash操作
            rehash();  
        HashEntry<K,V>[] tab = table;  
        int index = hash & (tab.length - 1); //3、定位key在table中的位置
        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;  
        if (e != null) {//4、如果e!=null表示table[index]中已经存在与之相等的key,替换value
            oldValue = e.value;  
            if (!onlyIfAbsent)  
                e.value = value;  
        }  
        else { //5、否则将该key-value作为头结点添加到table[index]中
            oldValue = null;  
            ++modCount;  
            tab[index] = new HashEntry<K,V>(key, hash, first, value);  
            count = c; // write-volatile  
        }  
        return oldValue;  
    } finally {  
        unlock(); //释放锁
    }  
}  

执行过程

步骤描述
步骤一对所有访问这个段的put方法的线程加锁
步骤二如果Segment中的HashEntry的数量超过了threshold,那么执行rehash操作
步骤三定位key在table中的位置
步骤四如果e!=null表示table[index]中已经存在与之相等的key,替换value
步骤五否则将该key-value作为头结点添加到table[index]中
步骤六释放锁

get方法

V get(Object key, int hash) {  
    if (count != 0) { // read-volatile  
        HashEntry<K,V> e = getFirst(hash);  
        while (e != null) {  
            if (e.hash == hash && key.equals(e.key)) {  
                V v = e.value;  
                if (v != null)  
                    return v;  
                return readValueUnderLock(e); // recheck  
            }  
            e = e.next;  
        }  
    }  
    return null;  
} 
HashEntry<K,V> getFirst(int hash){
    return tab[hash & (tab.length-1)];
}
V readValueUnderLock(HashEntry<K,V> e) {  
    lock();  
    try {  
        return e.value;  
    } finally {  
        unlock();  
    }  
}  

对于get方法,如果一个线程执行get方法,二另一个线程执行put方法,如果put的key-value是Map中已存在的key,那么put方法替换后的value值肯定能够被get方法看到(根据volatile读写之间的happens before关系可以知道)。但是如果线程执行的put方法put一个key-value是当前Map中不存在的,那么get方法可能获取不到新添加的key-value。因此HashMap是一弱一致性的。详细情况请见http://ifeve.com/concurrenthashmap-weakly-consistent/

参考文章
http://www.iteye.com/topic/344876
http://ifeve.com/concurrenthashmap-weakly-consistent/
https://my.oschina.net/hosee/blog/639352#h2_9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值