• ConcurrentHashMap 从Java7 到 Java8的改变


    一、关于分段锁

    1.分段锁发展概况

    集合框架很大程度减少了java程序员的重复劳动。在Java多线程环境中,以线程安全的方式使用集合类是一个首先考虑的问题。

    能够保证线程安全的哈希表中,ConcurrentHashMap是大家都熟知的,也知道它内部使用了分段锁。然而,进入到Java8时代,分段锁成为了历史。

    2.新版本ConcurrentHashMap

    在Java8的ConcurrentHashMap中,分段锁仅用来处理对象流。

    Java7中,Segment继承于ReentrantLock使用了显示锁,在Segment的实例方法中,每个更新操作内部又使用Unsafe来处理更新。这显然是一种浪费。显示锁、Unsafe 这二者都是可以保证对对象的原子操作。使用一个即可。

    Java7中的数据存储在数组 final Segment<K,V>[] segments; 这个一个特定大小的Segment数组,

    Segment继承于ReentrantLock 故而可以在更新操作时使用显示锁。

    二、Java7的ConcurrentHashMap

    java7中,ConcurrentHashMap的内部类Segment.

    Segment继承了ReetrantLock,利用了显示锁,同时在更新操作中也使用了Unsafe.双管齐下来保证线程安全。

    static final class Segment<K,V> extends ReentrantLock implements Serializable {
            private static final long serialVersionUID = 2249069246763182397L;
            // tryLock()最多等待时间
            static final int MAX_SCAN_RETRIES =
                Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
            // 分段表,元素通过entryAt/setEntryAt这两个方法提供了瞬时操作。
            transient volatile HashEntry<K,V>[] table;
            // 元素数量,通过锁或可见性地瞬时读取
            transient int count;
            // 修改次数
            transient int modCount;
    		// 表大小超出threshold时,重新哈希运算。它的值 = (int)(capacity*loadFactor)
            transient int threshold;
    		// 负载因子
            final float loadFactor;
            final V put(K key, int hash, V value, boolean onlyIfAbsent) {
                HashEntry<K,V> node = tryLock() ? null :
                    scanAndLockForPut(key, hash, value);
                V oldValue;
                try {
                    HashEntry<K,V>[] tab = table;
                    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))) {
                                oldValue = e.value;
                                if (!onlyIfAbsent) {
                                    e.value = value;
                                    ++modCount;
                                }
                                break;
                            }
                            e = e.next;
                        }
                        else {
                            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;
            }
    
            /**
                移除:value为null时,只匹配key,否则,两个都匹配
             */
            final V remove(Object key, int hash, Object value) {
                if (!tryLock())
                    scanAndLock(key, hash);
                V oldValue = null;
                try {
                    HashEntry<K,V>[] tab = table;
                    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)
                                  //  这里是cas操作
                                    setEntryAt(tab, index, next);
                                else
                                    pred.setNext(next);
                                ++modCount;
                                --count;
                                oldValue = v;
                            }
                            break;
                        }
                        pred = e;
                        e = next;
                    }
                } finally {
                    unlock();
                }
                return oldValue;
            }
    
            final void clear() {
                lock();
                try {
                    HashEntry<K,V>[] tab = table;
                    for (int i = 0; i < tab.length ; i++)
                    // Unsafe 操作    
    				setEntryAt(tab, i, null);
                    ++modCount;
                    count = 0;
                } finally {
                    unlock();
                }
            }
        }                                                   

    三、Java8的ConcurrentHashMap

    Java8中,ConcurrentHashMap较之前版本有了很大的改变。

    使用Node数组替代了Segment数组来存储数据。Node数组中不再使用显示锁,而是Unsafe的乐观锁机制。

    Segment予以保留,仅用来处理对象流的读写。

    从如下Java8版本的ConcurrentHashMap$Segment源码来看,分段锁,基本弃用了。

        static class Segment<K,V> extends ReentrantLock implements Serializable {
            private static final long serialVersionUID = 2249069246763182397L;
            final float loadFactor;
            Segment(float lf) { this.loadFactor = lf; }
        }
    

      

  • 相关阅读:
    居敬持志
    测试内容
    TestMarkDown
    git
    面试题
    兼容的可视区高度和宽度
    JS(数据类型、预解析、闭包、作用域、this)
    JavaScript new 一个构造函数
    Windows下gm打水印老是报gm convert: Unable to read font (n019003l.pfb)问题
    如何开始一个vue+webpack项目
  • 原文地址:https://www.cnblogs.com/zhengwenqiang/p/8185057.html
Copyright © 2020-2023  润新知