Android ConcurrentHashMap

参考小狼的简述blog http://www.jianshu.com/p/fb6e91b013cc
http://blog.csdn.net/qq924862077/article/details/74530103
参考mickole的blog http://www.cnblogs.com/mickole/articles/3757278.html
本文只用来作为个人总结
Java1.8的ConcurrentHashMap

一、使用CAS和Synchronized完成同步机制

1.Synchronized在java8中进行了很多优化性能提升很多
2.CAS:比较并替换 是一种硬件级别的操作。主要有三个概念:CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。在Java中CAS操作的核心类Unsafe。
ConcurrentHashMap中的三个CAS方法

 //判断内存的offset位置是否存在一样的tab元素,从指定的内存位置获取对象保证table是最新的
 @SuppressWarnings("unchecked")
    static final  Node tabAt(Node[] tab, int i) {
        return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

   判断obj对象的offset位置的值是否和expect相同,如果相同就用update替换
    static final  boolean casTabAt(Node[] tab, int i,
                                        Node c, Node v) {
        return U.compareAndSwapObject(object, offset, expect, update);
    }

    static final  void setTabAt(Node[] tab, int i, Node v) {
        U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
    }

二、ConcurrentHashMap的重要概念

1.table:装载node节点的数组,采用懒加载的方式在第一次put数据的时候初始化,长度永远是2的幂数,默认是0
2.nextTable:在调整table大小的时候用到
3.sizeCtl:记录数组扩容和初始化的标识位,-1表示正在初始化,-(1+n)表示有n个活跃的线程正在修改table
4.ForwardingNode 一个hash值为-1( 0x8fffffff)的特殊节点

三、初始化table

1.hashTable容器的初始化是是在数组第一次put值的时候

Android ConcurrentHashMap_第1张图片
image.png

2.initTable
当前ConcurrentHashMap的Table一定要是空的,这时候通过sizeCtl---记录table修改状态的标志位来判断是否可以初始化,如果sizeCtl<0 代表至少有一个线程在修改table。第一次修改时,会使用Usafe的compareAndSwapInt(对比覆盖的方法修改,防止其他线程同时修改过,这样保证只能有一个线程修改SIZECTl成功)将sizeCtl修改为-1。

    private final Node[] initTable() {
        Node[] tab; int sc;
        //1.判断容器是否为空
        while ((tab = table) == null || tab.length == 0) {
           //sizeCtl是否小于0,小于0代表其他线程正在修改
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
            //判断当前线程副本SIZECTL值是否和主存中一样,如果一样当前线程可以修改sizeCtl为-1,并返回true;
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node[] nt = (Node[])new Node[n];
                        table = tab = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

插入方法

插入方法实际上是由putVal来完成的,首先保证key和value不能为空。遍历整个容器,在容器指定位置插入值。
f:当前位置的node
n:容器的长度
i:要插入的位置
fh:当前位置元素的hash值
1.spread计算插入的位置
2.遍历容器
3.如果table为空需要初始化容器
4.使用tabAt方法判断table在(n-1)&hash这个位置是否插入过元素,如果没有f为空,那么在这个位置插入node元素,tabAt内部使用Usafe方法,保证table和主存的内容一致。
5.如果检测到有其他线程正在扩容,触发helpTransfer方法协助其他线程一同扩容
6.锁住当前的node,进行插入操作,这样只锁住一段代码相比于HashTable的方法锁性能上有很高提升。
7.如果发现一样的对象覆盖这个对象。如果没有在该位置插入一个新的node
8.如果是树,以树的方式插入。
9.addCount进行扩容

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[] tab = table;;) {
            Node 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(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 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 pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node p;
                            binCount = 2;
                            if ((p = ((TreeBin)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;
    }

get方法

它的get方法是没有用锁的,支持并发访问。通过CAS保证和主存中存在的table一致,再从这个table中遍历node查找到符合要求的数据

  public V get(Object key) {
        Node[] tab; Node e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

你可能感兴趣的:(Android ConcurrentHashMap)