ConcurrentHashMap学习笔记(Java8)

来源:互联网 发布:sql merge into 编辑:程序博客网 时间:2024/05/17 23:29

ConcurrentHashMap是Concurrent包里提供的一个解决并发问题的HashMap,试着从源代码来认识这个类


首先我们来看一下ConcurrentHashMap中两个重要的内部类:Node(节点)和Segment(桶)


Node的源代码如下


    static class Node<K,V> implements Map.Entry<K,V> {        final int hash;        final K key;        volatile V val;        volatile Node<K,V> next;        Node(int hash, K key, V val, Node<K,V> next) {            this.hash = hash;            this.key = key;            this.val = val;            this.next = next;        }        public final K getKey()       { return key; }        public final V getValue()     { return val; }        public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }        public final String toString(){ return key + "=" + val; }        public final V setValue(V value) {            throw new UnsupportedOperationException();        }        public final boolean equals(Object o) {            Object k, v, u; Map.Entry<?,?> e;            return ((o instanceof Map.Entry) &&                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&                    (v = e.getValue()) != null &&                    (k == key || k.equals(key)) &&                    (v == (u = val) || v.equals(u)));        }        /**         * Virtualized support for map.get(); overridden in subclasses.         */        Node<K,V> find(int h, Object k) {            Node<K,V> e = this;            if (k != null) {                do {                    K ek;                    if (e.hash == h &&                        ((ek = e.key) == k || (ek != null && k.equals(ek))))                        return e;                } while ((e = e.next) != null);            }            return null;        }    }

其中构造函数中需要对四个成员变量进行初期化:Key,Value,hash(散列值),next(下一个节点)

需要注意的几个方法是

setValue()时直接抛出以一个异常?(UnsupportedOperationException)

hashCode()返回该节点的散列值,该散列值为key与value的散列值按位与算出

equalsIO方法需要判断key值相同且value值相同

find()方法根据散列值及key值,遍历所有节点值取得对象节点,没有取得相应节点时返回null

其中setValue()与Hashmap不同,find()为新增方法,另外,java8中HashMap引入了树结构,在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; }    }

作为ConcurrentHashMap重要的内部类,Segment的代码只有三行,需要注意的是继承了ReentrantLock方法,可以认为Segment也是一种锁结构,构造函数中初始化了负载因子。

Segment的代码在Java8中极大地简化。相对应的是Java8版本的ConcurrentHashMap中出现了TreeNode和TreeBin结构。应该是利用红黑树进行了数据存储。仅红黑树这一节就足够写一篇很长的笔记了,我们先把它放在一边,先看核心的get/set机能

    /**     * Maps the specified key to the specified value in this table.     * Neither the key nor the value can be null.     *     * <p>The value can be retrieved by calling the {@code get} method     * with a key that is equal to the original key.     *     * @param key key with which the specified value is to be associated     * @param value value to be associated with the specified key     * @return the previous value associated with {@code key}, or     *         {@code null} if there was no mapping for {@code key}     * @throws NullPointerException if the specified key or value is null     */    public V put(K key, V value) {        return putVal(key, value, false);    }    /** Implementation for put and putIfAbsent */    final V putVal(K key, V value, boolean onlyIfAbsent) {        if (key == null || value == null) throw new NullPointerException();        int hash = spread(key.hashCode());        int binCount = 0;        for (Node<K,V>[] tab = table;;) {            Node<K,V> f; int n, i, fh;            if (tab == null || (n = tab.length) == 0)                tab = initTable();            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {                if (casTabAt(tab, i, null,                             new Node<K,V>(hash, key, value, null)))                    break;                   // no lock when adding to empty bin            }            else if ((fh = f.hash) == MOVED)                tab = helpTransfer(tab, f);            else {                V oldVal = null;                synchronized (f) {                    if (tabAt(tab, i) == f) {                        if (fh >= 0) {                            binCount = 1;                            for (Node<K,V> e = f;; ++binCount) {                                K ek;                                if (e.hash == hash &&                                    ((ek = e.key) == key ||                                     (ek != null && key.equals(ek)))) {                                    oldVal = e.val;                                    if (!onlyIfAbsent)                                        e.val = value;                                    break;                                }                                Node<K,V> pred = e;                                if ((e = e.next) == null) {                                    pred.next = new Node<K,V>(hash, key,                                                              value, null);                                    break;                                }                            }                        }                        else if (f instanceof TreeBin) {                            Node<K,V> p;                            binCount = 2;                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,                                                           value)) != null) {                                oldVal = p.val;                                if (!onlyIfAbsent)                                    p.val = value;                            }                        }                    }                }                if (binCount != 0) {                    if (binCount >= TREEIFY_THRESHOLD)                        treeifyBin(tab, i);                    if (oldVal != null)                        return oldVal;                    break;                }            }        }        addCount(1L, binCount);        return null;    }

ConcurrentHashMap与HashMap的一个区别就是,ConcurrentHashMap不允许Key值为null,为null的时候会直接抛出一个空指针异常


未完待续



0 0
原创粉丝点击