Java concurrency -- Concurrent HashMap and CopyOnWriteArrayList


1.1. Concurrent HashMap of jdk1.7

The data structure of ConcurrentHashMap in jdk1.7 sets another layer of array in the outer layer of the real HashMap, which is equivalent to dividing an entire HashMap and using a segmentation lock. When a data is put in, the lock of segment will be obtained first, and then the data can be written, as shown in the following figure:

1.2. Concurrent HashMap of jdk1.8

jdk1.8 controls the insecurity of concurrency based on segment lock and cas, and adopts a lock free state. The efficiency is much higher than that of 1.7. The code is as follows:
We can see that the method casTabAt in the code is to control the writing of data in the case of multithreading, followed by the construction of linked list and red black tree.
The process is as follows:
1. If the array hashMap is not initialized, it is initialized.
2. The cas algorithm is used to compete for writing data. Only one thread can compete successfully at a time. It loops many times until the node is put in.
3. If the first competition is not successful, the second cycle finds that the array is being expanded and will assist in the expansion. See 1.2.1 for details.
4. When cas is replaced, tabat (tab, I = (n - 1) & hash) returns the column in which data needs to be written, and locks the column in which the linked list or red black tree is to be built to prevent node loss.
5. If the conditions of the linked list are met, the linked list will be built. If the conditions of the red black tree are met, the red black tree will be built.

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();
        // This logic is entered when multiple threads compete for a slot
        // Suppose there are two threads T1 and T2 at this time. T1 competes successfully, and T2 loops again, so we have to follow the following logic
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        	// Using cas algorithm, only one can write data successfully
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        // When you find that hashmap is expanding when you want to write data, the expansion assistance in 8.2.1 will occur
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        // This is normal walking
        else {
            V oldVal = null;
            // Lock the column of the linked list or red black tree to be built, for fear that multiple threads will come to this logic at the same time,
            // If they conflict with each other, it is easy to see the phenomenon shown in the figure below. If you just link to one node and change to another node, you will lose the node
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        // Circular mode to build linked list
                        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;
                            Node<K,V> pred = e;
                            if ((e = == null) {
                       = new Node<K,V>(hash, key,
                                                          value, null);
                    // Build red black tree
                    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;
    addCount(1L, binCount);
    return null;

1.2.1 capacity expansion assisted by JDK1.8 HashMap

Capacity expansion assistance means that when multiple threads insert nodes into slots, it is found that the slots have been occupied when executing the first round of loop, and the current HashMap is expanding when executing the second round of loop. At this time, the current thread will assist in capacity expansion and divide it into 16 slots to copy the original data.
This is the code above:

// When you find that hashmap is expanding when you want to write data, the expansion assistance in 8.2.1 will occur
else if ((fh = f.hash) == MOVED)
    tab = helpTransfer(tab, f);


CopyOnWriteArrayList is a thread safe array with read-write separation and space for time to avoid fierce lock competition to ensure concurrency safety.
Application scenario: read more and write less.
add method process: when adding a data, the original array will be copied. The copied array will be 1 longer than the original array and locked. At the same time, only one thread can copy the array. The original array is only used for reading and the new array is used for writing. Therefore, the reading and writing are separated. After writing, the original array will be discarded and the new array will be used as the current array, The code is as follows:

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    try {
        Object[] elements = getArray();
        int len = elements.length;
        // Make a copy and add 1
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        // Discard the original array
        return true;
    } finally {

Tags: Java

Posted on Thu, 14 Oct 2021 22:26:44 -0400 by brockie99