zoukankan      html  css  js  c++  java
  • jdk源码ConcurrentHashMap——jdk1.7

     首先做个分析:

    hashMap,hashTable,ConcurrentHashMap,这三者之间的区别,HashMap是线程不安全的,在多线程的环境下,hashMap的put方法可能引起死循环,于是为了线程安全,出现了hashTable,hashTable解决多线程安全的问题是简单粗暴的加synchronized关键字,但是这种方法引起效率低下,于是ConcurrentHashMap出现了,下面主要介绍下ConcurrentHashMap。

    ConcurrentHashMap实现的接口是ConcurrentMap

    public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
            implements ConcurrentMap<K, V>, Serializable {

     成员变量,这里只列举出了ConcurrentHashMap特有的

       static final int DEFAULT_CONCURRENCY_LEVEL = 16; //初始的并发等级,通过并发等级来确定Segment的大小

       static final int MIN_SEGMENT_TABLE_CAPACITY = 2;// segment的最小值

        static final int MAX_SEGMENTS = 1 << 16; //segment的最大值

     ConcurrentHashMap的构造函数

     public ConcurrentHashMap(int initialCapacity,
                                 float loadFactor, int concurrencyLevel) {
            if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
                throw new IllegalArgumentException();
            if (concurrencyLevel > MAX_SEGMENTS)
                concurrencyLevel = MAX_SEGMENTS;
            // Find power-of-two sizes best matching arguments
            int sshift = 0;//计算ssize左移的次数
            int ssize = 1;//计算segment的大小
            while (ssize < concurrencyLevel) {//segment的大小为2^n
                ++sshift;
                ssize <<= 1;
            }
            this.segmentShift = 32 - sshift;
            this.segmentMask = ssize - 1;//这个为什么要为Segment数组的长度 -1,主要是为了让低位为1,这样在做&运算确定Segment的索引时能够更加分散
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            int c = initialCapacity / ssize;//计算每个segment的大小
            if (c * ssize < initialCapacity)//若是条件成立,表示c有余数,所以增加一个segment
                ++c;
            int cap = MIN_SEGMENT_TABLE_CAPACITY;
            while (cap < c)
                cap <<= 1;
            // create segments and segments[0]//创建一个segment,并且放在 segment[ 0]
            Segment<K,V> s0 =
                new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                                 (HashEntry<K,V>[])new HashEntry[cap]);
            Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
            UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
            this.segments = ss;
        } 

    Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
                this.loadFactor = lf;
                this.threshold = threshold;
                this.table = tab;
            } 

    看完ConcurrentHashMap的构造函数,我们应该对该map的数据结构有个大致了解,如下:

     put方法,首先对key值第一次hash确定segment的位置,然后在segment内部获取锁,接着key第二次hash,确定hashEntry在table中的位置,然后put操作和hashMap相同,最后关闭锁

     public V put(K key, V value) {//找到对应的segment,吧key,value放入对应的segment中
            Segment<K,V> s;
            if (value == null)
                throw new NullPointerException();
            int hash = hash(key);
            int j = (hash >>> segmentShift) & segmentMask;
            if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
                 (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
                s = ensureSegment(j);
            return s.put(key, hash, value, false);
        }

     final V put(K key, int hash, V value, boolean onlyIfAbsent) {

    //每个segment进行put操作的时候都要进行加锁操作
                HashEntry<K,V> node = tryLock() ? null :
                    scanAndLockForPut(key, hash, value);
                V oldValue;
                try {
                    HashEntry<K,V>[] tab = table;//table为segment所连接的hashEntry数组,
                    int index = (tab.length - 1) & hash;
                    HashEntry<K,V> first = entryAt(tab, index);//找到数组对应下标的链表
                    for (HashEntry<K,V> e = first;;) {
                        if (e != null) {//头结点不为空
                            K k;
                            if ((k = e.key) == key ||
                                (e.hash == hash && key.equals(k))) {//若是key相同则替换
                                oldValue = e.value;
                                if (!onlyIfAbsent) {
                                    e.value = value;
                                    ++modCount;
                                }
                                break;
                            }
                            e = e.next;
                        }
                        else {//把当前node插入到table[i]的位置
                            if (node != null)
                                node.setNext(first);
                            else
                                node = new HashEntry<K,V>(hash, key, value, first);
                            int c = count + 1;
                            if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                                rehash(node);
                            else
                                setEntryAt(tab, index, node);
                            ++modCount;
                            count = c;
                            oldValue = null;
                            break;
                        }
                    }
                } finally {
                    unlock();
                }
                return oldValue;
            } 

    remove()操作

    public V remove(Object key) {
            int hash = hash(key);
            Segment<K,V> s = segmentForHash(hash);//找到对应的segment
            return s == null ? null : s.remove(key, hash, null);
        }

        final V remove(Object key, int hash, Object value) {
                if (!tryLock())//获取锁
                    scanAndLock(key, hash);
                V oldValue = null;
                try {
                    HashEntry<K,V>[] tab = table;//table是segment所连接的hashEntrt数组
                    int index = (tab.length - 1) & hash;
                    HashEntry<K,V> e = entryAt(tab, index);//找到链表头
                    HashEntry<K,V> pred = null;//记录删除节点的前一节点
                    while (e != null) {
                        K k;
                        HashEntry<K,V> next = e.next;
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            V v = e.value;
                            if (value == null || value == v || value.equals(v)) {
                                if (pred == null)//删除头结点
                                    setEntryAt(tab, index, next);
                                else
                                    pred.setNext(next);
                                ++modCount;
                                --count;
                                oldValue = v;
                            }
                            break;
                        }
                        pred = e;
                        e = next;
                    }
                } finally {
                    unlock();
                }
                return oldValue;
            }

  • 相关阅读:
    vue+filesaver+xlsx导出table为excel
    layer弹窗第一次不居中。然后再点击就居中了。解决办法
    GitHub的Fork 是什么意思
    什么时候会发送options请求
    实现H5连接分享给好友或朋友圈自定义分享内容(标题、图片、简介)的方法代码
    vue的element的table表头自定义添加按钮
    小程序配置体验版添加参数
    迅为iMX8M Mini开发板硬件接口原理分析
    迅为i.MX8MM 核心板引脚说明(按功能划分)
    迅为3399开发板Ubuntu 系统双屏异显,双屏同显测试
  • 原文地址:https://www.cnblogs.com/wanglingdeboke/p/9718277.html
Copyright © 2011-2022 走看看