Location>code7788 >text

Source code analysis of why to use ConcurrentHashMap

Popularity:665 ℃/2024-08-21 14:11:05

Why use ConcurrentHashMap?

ConcurrentHashMapis a thread-safe program under the JUC package.HashMapclass, we all know that multi-threaded scenarios are going to use theConcurrentHashMapin place ofHashMapuse, ever wondered why it can't be usedHashMapWhy does it work?ConcurrentHashMapWhat about it? I'll take you through some of the details by walking through the source code!

HashMap

mapA type of array.JDK1.8hit the nail on the headHashMapin the form of arrays + linked lists/red-black trees, not too much explanation here

When we are executing a multithreaded task, if the resource being operated on is theHashMaptype can lead to concurrency exceptions in the program, as shown in Figure

check on sthnextNodeThe reason for going to the source code in this method is obvious, as shown in Figure

So what do the two variables in the if judgment do, and why should they throw an exception if they are not the same?

First, let's look at what we've learned from creating theHashmapWhat was done between the time the variable was created and the time the exception was thrown?

The code for the whole process is as follows

public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
        for (int i = 0;i < 20; i++){
            new Thread(()-> {
                (().getName(),new Date().toString());
                (map);
            },(i)).start();
        }
    }

Let's click in.HashMapcan be seen in the constructor method of the method, which is only given hereloadFactorThis variable an initial value, which we know as the loading factor 0.75

Let's go over what the put method does.

put->putVal

When we click on it, we see that this is the first variable in the if judgment we saw above. If we go to the definition of the variable, we can see that it's an int and not assigned a value.

We can get an initial conclusion by this, when we execute the put method every time, this value will perform a +1 operation

As you can see below there is another one calledafterNodeInsertionmethod, we clicked on it and found that it was an empty method, and then read the comments on it, which probably meant to give theLinkedHashMapThe methods of the callbacks provided by theLinkedHashMapbeHashMapA subclass of

Then we tapprintlnmethodologies

println->valueOf->toString

Find usHashMaprewrittentoStringmethod, we find that theHashMapdid not find the rewrittentoStringmethod, so let's just go ahead and search for his parent class

Search process, if you use Ctrl + f to search the entire class, you will see a toString query record, here for the HashMap's internal class Node's method, not the HashMap's

discoveriesAbstractMapThe class really does override thetoStringmethodologies

Here we finally see the iterator, and we return to theHashMapThe class went to look at it.entrySetWhat did the method do?

Because we walked through the put method, at this point theHashMaphit the nail on the headentrySetis content, you can see here that it is a direct translation of theentrySetIt's back.

entrySetis actually a Set collection that will be ourHashMapThe storage unit Entry is placed inside the ( )

We then find the location that implements the iterator method in the collection interface via a generic, anotherHashMapThe inner class of theEntrySet

Finding yet another class we haven't seen before, we click on it in passing

click onnextNodemethod and found that, hey, this is not the place to report errors, to the point that now wemodCountThere too, the node that reported the error has been found, one more to go.expectedModCountI didn't find it, so after a quick check, it occurred to me that it could be in one of the constructor methods, and I went back to look at theEntryIteratorI didn't write my own constructor, so I opened his parent class, and it was clear, just look at the picture.

Here, these two buddies finally found all, so here comes the problem again, to theexpectedModCountThe endowment ismodCountIt's worth it. How can these two guys be different?

The answer also surfaced, single-threaded is certainly not a problem, but we are a multi-threaded operation, ah, if the A thread has just finished giving the value, the B thread runningputValWay to go, run throughmodCount+1 now, and then Thread A goes immediately afterward tonextNodemethod, a comparison is wrong, and then an exception is thrown

so to speakHashMapIt's still very problematic in concurrent scenarios

ConcurrentHashMap

HashMapIn the multi-threaded scenarios can not be used, not safe ah, so adapted to multi-threaded thread-safe HashMap:ConcurrentHashMaparise at an opportune time

Then we take the original code'sHashMapexchange (sth) for (sth else)ConcurrentHashMap

public static void main(String[] args) {
        ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
        for (int i = 0;i < 20; i++){
            new Thread(()-> {
                (().getName(),new Date().toString());
                (map);
            },(i)).start();
        }
    }

Executed N times, indeed, no error, each loop can print successfully, so amazing things, let's see why he does not report an error

First, click on this class to go in and take a look at it

It was found that this class also inherits theAbstractMapclass, describing the relationship with theHashMapis the division of the same ah, and then we went to find the upper two brothers found that the time, disappeared, and then go to find the iterator and next method found that a completely different set, so naturally, not like this error, then how he is dealing with multi-threaded operation scenarios it

ConcurrentHashMapattributablesynchronized + CAS algorithm to implement the thread-safe

If you go to the source code, you'll see that theConcurrentHashMapThere's a lot in there.+ data type writing, this writing is the use of CAS algorithm to achieve lock-free modification of the value of the operation, this algorithm can greatly reduce the locking process caused by the performance losses

The algorithm is probably to keep going with the value of the variable in memory and the value of the variable expected by the code is the same, if it is the same will be modified successfully, if not the same will refuse to execute the modification, with this way to determine the current thread in the current thread is the most recent value, if not may be overwritten by the results of other threads

Because of the way the algorithm determines this, if a thread changes the value and then changes it back, the algorithm still assumes that it is the latest value that has not been changed

And by looking at the source code, it was found that the operation of theNodeThe related object will be used with thesynchronizedLock the object