The last post was Scala – Definition of Classes. This post shares the Source code for ConcurrentHashMap. Today update part of the first, later to go out, understanding.

ConcurrentHashMap source code interpretation a

Let’s start with a few global variables

private static final int MAXIMUM_CAPACITY = 1 << 30; Private static final int DEFAULT_CAPACITY = 1; private static final int DEFAULT_CAPACITY = 1; Private static final float LOAD_FACTOR = 0.75f; Static final int TREEIFY_THRESHOLD = 8; Static final int UNTREEIFY_THRESHOLD = 6; static final int UNTREEIFY_THRESHOLD = 6; Static final int MIN_TREEIFY_CAPACITY = 64; Private static final int MIN_TRANSFER_STRIDE = 16; private static int RESIZE_STAMP_BITS = 16; Private static final int MAX_RESIZERS = (1 << (32-resize_stamp_bits)) - 1; private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS; static final int MOVED = -1; Static final int TREEBIN = -2; static final int TREEBIN = -2; // Roots of trees hash = -1; static final int RESERVED = -1; // Hash value of the transient reservations reservations // usable bits of normal node hash Static final int HASH_BITS = 0x7FFFFF; static final int HASH_BITS = 0x7FFFFF; static final int NCPU = Runtime.getRuntime().availableProcessors(); // Number of available processorsCopy the code

Then there are a few global properties

transient volatile Node<K,V>[] table; Private TRANSIENT Volatile Node<K,V>[] nextTable; // Next table pointed to by ForwardNode, array being expanded (not yet used) private TRANSIENT Volatile long baseCount; The default value is 0. When the value is -1, it indicates that a thread is expanding capacity. When the value is -n, it indicates that n threads are expanding capacity. Private TRANSIENT volatile int sizeCtl; private transient volatile int sizeCtl; private transient volatile int sizeCtl; Private TRANSIENT volatile int transferIndex; // Private TRANSIENT volatile int cellsBusy; // If the CAS calculation fails, that is, if the current concurrency is high, then // will be counted using the CounterCell[] array, similar to jdk1.7 fragment lock, Private TRANSIENT volatile CounterCell[] counterCells; private transient volatile [] counterCells; // Count arrays.Copy the code

The first one is the parametrized construct, and here if theta

ConcurrentHashMap chm = new ConcurrentHashMap(15);
Copy the code

So the capacity is actually not 15, it’s 32;

public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }
Copy the code

So if you look at tableSizeFor, 15+7+1=23;

private static final int tableSizeFor(int c) { int n = c - 1; //23-1=22 0b10110 n |= n >>> 1; / / 10110 | 01011 = 11111, here are 11111 is 31 n | = n > > > 2. n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; / / + 1 = 31 32}Copy the code

So this is the final capacity, which is 32, which is twice the nearest power of 2 of the parameter that takes the parameter.

Next up is the put method

Final V putVal(K key, V value, Boolean onlyIfAbsent) {//onlyIfAbsent Is the same as hashMap. The default value is false. if (key == null || value == null) throw new NullPointerException(); ConcurrentHashMap (////) does not allow null values in ConcurrentHashMap. This is different from HashMap. Int hash = spread(key.hashcode ()); // This method is equivalent to calculating the hash value. int binCount = 0; For (Node<K,V>[] TAB = table;) { Node<K,V> f; int n, i, fh; / / a: if the array is empty or the length of 0, work to initialize the if (TAB = = null | | (n = TAB. Length) = = 0) TAB = initTable (); // If the node at the bucket location is empty, it is the first node insertion case, that is, there are no elements at the bucket location, use CAS to add elements. Else if ((f = tabAt(TAB, I = (n-1) & hash)) == null) {if (casTabAt(TAB, I, null), New Node<K,V>(hash, key, value, null)))// If the element is added successfully, break; // No lock when adding to empty bin} // No lock when adding to empty bin} // Case 3: If the hash value of the bucket location element is moved, that is, -1, it indicates that the capacity is being expanded. else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); // The hash bucket location element is not empty, and the current expansion operation is not performed, and the element is added // Case 4: If the bucket has elements, insert the bucket. There are two possibilities: first, the linked list corresponding to the bucket does not have the same key, and then insert the node node at the end of the list. Instead, it has the same key, and then replace its value. else { V oldVal = null; Synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) {synchronized (f) { If (tabAt(TAB, I) == f) {if (tabAt(TAB, I) == f) {if (tabAt(TAB, I) == f) {if (tabAt(TAB, I) == f) {if (tabAt(TAB, I) == f) {if (tabAt(TAB, I) == f) { So judge again. 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)))) {// To determine the node, if all the same, then overwrite the old value. oldVal = e.val; if (! onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; If ((e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) {// If (e = e.ext) == null) { pred.next = new Node<K,V>(hash, key, value, null); break; }}} else if (f instanceof TreeBin) {// Add elements to TreeBin; binCount = 2; if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) ! = null) { oldVal = p.val; if (! onlyIfAbsent) p.val = value; } } } } if (binCount ! If (binCount >= TREEIFY_THRESHOLD)// If (binCount >= TREEIFY_THRESHOLD)// If (binCount >= TREEIFY_THRESHOLD)// If (binCount >= TREEIFY_THRESHOLD)// If (binCount >= TREEIFY_THRESHOLD)// If (binCount >= TREEIFY_THRESHOLD) There is also a test to determine whether the array is less than 64, if it is less than 64, it will not be // treed. It's just array expansion. treeifyBin(tab, i); if (oldVal ! = null) // If the key is repeated, return oldVal; break; } } } addCount(1L, binCount); // Add a new element, maintain the set length, and determine whether to expand the operation return null; }Copy the code

Summary: In the case of concurrent map, jdK1.8, the underlying layer is the same as hashMap, which is an array plus a linked list plus a red-black tree.

  • This is ConcurrentHashMap source code interpretation 1.

  • Also welcome everybody exchange discussion, if this article has incorrect place, hope everybody many forgive.

  • Your support is my biggest motivation, if you help out, give me a thumbs up