"Java Source Analysis": Concurrenthashmap Jdk1.8__java

Source: Internet
Author: User
Tags cas static class volatile lockstate
"Java Source analysis": Concurrenthashmap JDK1.8

Recently has been looking at the source of the j.u.c, understanding the atomic operation, understanding the lock mechanism, understanding multithreading concurrency and so on. But Concurrenthashmap has dragged on until today.

Also want to thank Concurrenthashmap this class, just started to understand the working principle inside, but, Helpless to see the internet about the introduction of CONCURRENTHASHMAP this kind of information or blog is based on JDK1.8 before, and just this kind of JDK1.8 after a great change. Therefore, because it involves about the atomic operation of CAs, I did not know what it was before, so began to understand the atomic operation, looked at the Java.util.concurrent.atom package related to the source code has a certain understanding. Then in order to understand the lock mechanism, looked at the Java.util.concurrent.lock package related class library, the lock mechanism has a general understanding of the thread pool-related classes, the thread pool also has a certain understanding.

About the blocking queue related classes, they also roughly read, but did not form a corresponding blog, later have time to get to know their time before recording it. The whole process took me nearly one months, although the internal implementation of the Library has been a general understanding, but the harvest is still quite a lot. Let's get a better idea of how they work in multithreaded concurrency.

Back to the point, just by today's Sunday, spent nearly a day to see the realization of the principle of concurrenthashmap, finally saw a probably, have a general understanding. And there's this blog post. Concurrenthashmap before the JDK1.8 version of the implementation of the principle

Now that the title of this blog is clearly labeled based on the JDK1.8 version, it implies that this version and previous versions of the Concurrenthashmap are slightly different, right. X

Below we first use the information on the Internet to see the previous version of the Concurrenthashmap of the implementation of ideas.

We all know that HashMap is not thread safe. Hashtable is thread-safe. We have seen the Hashtable source code, we all know Hashtable thread safety is in each method to add the Synchronized keyword to decorate, that is, Hashtable is for the entire table lock, As a result, the Hashtable container is inefficient in a highly competitive concurrency environment.

The reason for inefficiency is more detailed: because all threads accessing Hashtable must compete for the same lock. When one thread accesses the Hashtable synchronization method, other threads may enter a blocking or polling state when they access the Hashtable synchronization method. If thread 1 adds elements using put, thread 2 does not only use the Put method to add elements, but also cannot use the Get method to obtain elements, so the more intense the competition the less efficient.

Based on the shortcomings of Hashtable, people began to think, if there are many locks in the container, each lock for the lock container part of the data, then when multithreading access to different data segments in the container data, the thread will not exist lock competition, which can effectively improve the efficiency of concurrent access. This is our "Lock separation" technology, which is also the basis of CONCURRENTHASHMAP implementation.

Concurrenthashmap uses the lock segmentation technique, Concurrenthashmap consists of multiple segment (segment contains a lot of node, which is our key value pair), each segment has a lock to achieve thread safety, When a thread occupies a lock to access one segment of data, data from other segments can also be accessed by other threads.

Therefore, the concurrenthashmap is transformed into a study of segment. This is because, Concurrenthashmap's get and put operation is directly entrusted to the segment to the got, the put method, but their own hands-on JDK1.8 specific implementation does not want to be introduced on the Internet these posts. So, there is an introduction to this blog post.

Recommend a few JDK1.8 previous version of the principle of concurrenthashmap analysis, convenient for everyone to compare.

1, http://www.iteye.com/topic/344876

2, http://ifeve.com/concurrenthashmap/

If you need more, please search yourself online.

The following is an introduction to Concurrenthashmap in the JDK1.8 version. Concurrenthashmap Introduction in JDK1.8 version 1. Preface

The first points to illustrate:

1, JDK1.8 's Concurrenthashmap in segment, although retained, but has simplified properties, just to compatible with the old version.

2, the bottom of the Concurrenthashmap and Java1.8 HashMap have similarities, the bottom is still from the "array" + linked list + red-black tree to achieve, the underlying structure is to store the Treebin object, rather than TreeNode object;

3, CONCURRENTHASHMAP implementation of the use of more CAS algorithm, Unsafe.compareandswapint (this, valueoffset, expect, update); CAS (Compare and Swap), meaning that if the Valueoffset position contains the same value as the expect value, the value of the update Valueoffset position is update and returns TRUE, otherwise it is not updated and returns false.

Concurrenthashmap since the use of CAs to achieve non-blocking lock-free implementation of thread safety, then there is no use of locks it. Answer: Still use the Synchronized keyword to synchronize, where to use it. In the operation of the same hash value of the linked list of the head node will be synchronized locked, so as to ensure thread safety.

Read Concurrenthashmap the entire class of source code, to their own feeling is the same as the implementation of HashMap basically, when there is a modification operation with the help of the synchronized to Table[i] Locking guarantees thread safety and the use of CAs for atomic operations, others are basically consistent, for example: the Concurrenthashmap get (int key) method is implemented: according to the hash value of the key to find its corresponding position in the table I, The linked list (or tree) that is stored in the Table[i] location is then searched for a node that has a key and, if so, returns the value of the node, otherwise null is returned. The train of thought is not very familiar, and is not the same way as HashMap. So, if you are also looking at the source code of Concurrenthashmap, do not be afraid, the idea is the original idea, just a little bit more things. 2. Introduction of related properties in Concurrenthashmap class

To facilitate the introduction of later implementations of this class, you need to introduce some of the properties in this class first.

Sizectl one of the most important attributes, look at the source code before this attribute means, be sure to remember.

0. Private transient volatile int sizectl;//control identifier

This attribute is given in the source code as follows:

     /**
        * Table initialization and resizing control.  When negative, the
        * table was being initialized or resized:-1 for initialization,
        * Else-(1 + the number of ACTI ve resizing threads).  Otherwise,
        * When table was null, holds the initial table size to use upon
        * creation, or 0 for default. After initialization, holds the
        * Next element count value upon which to resize the table.
        */

Translated as follows:

Sizectl are control identifiers, and different values represent different meanings. A negative number represents an initialization or expansion operation. Where 1 represents an initialization,-n indicates that n-1 threads are expanding operations positive or 0 represents a hash table that has not been initialized, which represents the size of the initialization or next expansion, similar to the expansion threshold. Its value is always 0.75 times times the current concurrenthashmap capacity, which corresponds to the loadfactor. The actual capacity is >=sizectl, then the expansion.

1, transient volatile node<k,v>[] table; is an array of containers, initialized the first time the data is inserted, the size is the power of 2. This is what we call the underlying structure: "Array + linked list (or tree)"

2, private static final int maximum_capacity = 1 << 30; Maximum capacity

3, private static final intdefault_capacity = 16;

4, static final int max_array_size = integer.max_value-8; max_value=2^31-1=2147483647

5, private static finalint default_concurrency_level = 16;

6, private static final float load_factor = 0.75f;

7, static final int treeify_threshold = 8; The value of the linked list to the tree, if Table[i] below the chain table length is greater than 8 o'clock conversion number

8, static final int untreeify_threshold = 6; The valve value of the tree-chain table, less than or equal to 6, is converted into a linked list, only when the expansion of the tranfer can be a tree-linked list

9, static final int min_treeify_capacity = 64;

10, private static final int min_transfer_stride = 16;

11, private static int resize_stamp_bits = 16;

12, private static final int max_resizers = (1 << (32-resize_stamp_bits))-1; Maximum number of threads for help resize

13, private static final int resize_stamp_shift = 32-resize_stamp_bits;

14, static final int moved =-1; Hash for forwarding nodes (forwarding nodes hash value), marked bit

15, static final int treebin =-2; Hash for roots of trees (hash value for root node)

16, static final int RESERVED =-3; Hash for transient reservations (reservationnode hash value) 3, Concurrenthashmap constructor

As always, let's start with the constructor of the class.

    /** * Creates a new, empty map with the default initial table size (16).  */Public Concurrenthashmap () {} 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;
     } * * Creates a new map with the same mappings as the given map.
        * */Public Concurrenthashmap (MAP&LT; extends K,? extends v> m) {this.sizectl = default_capacity;
    Putall (m);
    Concurrenthashmap (int initialcapacity, float loadfactor) {This (initialcapacity, loadfactor, 1);  Public Concurrenthashmap (int initialcapacity, float loadfactor, int concurrencylevel) {if (!) ( Loadfactor > 0.0f) | | Initialcapacity < 0 | |
        Concurrencylevel <= 0) throw new IllegalArgumentException ();   if (Initialcapacity < concurrencylevel)//use at least as many bins initialcapacity = concurrencylevel;
        As estimated threads long size = (long) (1.0 + (long) initialcapacity/loadfactor);
            int cap = (size >= (long) maximum_capacity)?
        Maximum_capacity:tablesizefor ((int) size);
    This.sizectl = cap; }

There have been hashmap and Hashtable source experiences to see if these constructors are fairly easy to do.

The constructor above mainly does two things:

1, the validity of the inspection of parameters

2, the length of the initialization of the table (if you do not specify 16 by default).

Here's a parameter: Concurrencylevel, which represents the maximum number of threads that can update conccurenthashmap at the same time without creating a lock competition. The default value is 16 (that is, allowing 16 threads to be concurrent may not produce a competition). In order to ensure concurrent performance, we have to well estimate the Concurrencylevel value, or otherwise the competition is rather severe, causing the thread to attempt to write to the currently locked segment blocked. related node class in Concurrenthashmap class: Node/treenode/treebin 1, Node class

The node class is a storage element in the table array, that is, a node object represents a key value pair (key,value) stored in the table.

The node class is not provided with a modified entry (the only SetValue method throws an exception) and therefore can only be used for read-only traversal.

The specific code for this class is as follows:

    /* *node class is not provided to modify the entry (SetValue method throws an exception for subclasses to implement), that is, readable.
     Can only be used for read-only traversal.
        * * Static class Node<k,v> implements map.entry<k,v> {final int hash;
        Final K Key;

        Volatile V val;//volatile to ensure visibility 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;} /* The code in the Hashcode () method of the node class in HashMap is: Objects.hashcode (key) ^ Objects.hashcode (value) and Objects.hashco De (key) is also called Key.hashcode (), so the effect is the same.
        The wording is not the same;
        Public final int hashcode () {return Key.hashcode () ^ Val.hashcode ();}
        Public final String toString () {return key + "=" + val;} Public final V SetValue (v value) {//not allowed to modify value value, HashMap allow throw new UnsuppOrtedoperationexception (); }/* HashMap uses if (o = = this), and nested if;concurrenthashmap using && personally feel HashMap code better read and Solution/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. * Add the Find method auxiliary get method, HashMap in the Node class does not have this method * * 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;
 }
    }

When we look at this class, we can compare it with the specific code of the node class in HashMap, and find that there are some subtle differences in the implementation.

For example, the code for the hashcode in Concurrenthashmap.node is like this:

     Public final int hashcode ()   {return Key.hashcode () ^ Val.hashcode ();}

And the code for Hashmap.node's hashcode is this:

    Public final int hashcode () {return
                Objects.hashcode (key) ^ Objects.hashcode (value);
            }

and Objects.hashcode (key) is also called Key.hashcode (), therefore, the effect of the two is the same, the wording is not the same.

In addition to the Hashcode method, the Find method in the node class is different in the implementation of the two classes. 2, TreeNode

    * * Nodes for use in Treebins */static final class Treenode<k,v> extends Node<k,v> {  Treenode<k,v> parent;
        Red-black tree links treenode<k,v> left;
        Treenode<k,v> right;    Treenode<k,v> prev;

        needed to unlink next upon deletion Boolean red; TreeNode (int hash, K key, V Val, node<k,v> Next, treenode<k,v> parent) {Super (ha
            SH, Key, Val, next);
        This.parent = parent;
        node<k,v> Find (int h, Object K) {return Findtreenode (h, k, null);
         } * * Returns the TreeNode (or null if not found) for the given key * starting at given root. * According to the given key value from the root node to find the node */FINAL treenode<k,v> findtreenode (int h, Object K,
  Class<?> KC) {if (k!= null) {//hashmap no non-null judgment treenode<k,v> p = this;              do {int ph, dir; K PK;
                    Treenode<k,v> Q;
                    Treenode<k,v> PL = p.left, pr = p.right;
                    if (ph = P.hash) > h) p = pl;
                    else if (ph < h) p = PR; else if (pk = p.key) = = k | |
                        (PK!= null && k.equals (PK))
                    return p;
                    else if (pl = null) p = PR;
                    else if (pr = null) p = PL;
                              else if (KC!= null | |
                        (KC = comparableclassfor (k))!= null) && (dir = Comparecomparables (KC, K, PK))!= 0) p = (Dir < 0)?
                    PL:PR;
                    else if (q = Pr.findtreenode (h, K, KC)) return q!= null);
                else P = pl; } while(P!= null);
        return null; }
    }

Compared with HashMap, the TreeNode is fairly concise, and the Concurrenthashmap linked list does not go directly to the tree.
As the annotations (Nodes for use in treebins) say, just wrap these nodes into TreeNode and put them into Treebin,
Then the treebin to convert the red and black trees. The red-black tree does not understand does not matter, does not affect sees the Concurrenthashmap the interior realization 3, Treebins

Treebin is used to encapsulate and maintain TreeNode, including Puttreeval, Lookroot, Unlookroot, remove, balanceinsetion, balancedeletion, and so on, when the linked table turns tree, Used to encapsulate TreeNode, that is to say, concurrenthashmap red and black trees are stored treebin, not TreeNode.

The Treebins class code is too long to intercept some of the code as follows:

    Static final class Treebin<k,v> extends node<k,v> {treenode<k,v> root;
        Volatile treenode<k,v>;
        Volatile Thread waiter;
        volatile int lockstate; Values for lockstate static final int WRITER = 1; Set while holding write lock static final int waiter = 2; Set when waiting for write lock static final int READER = 4;
         Increment value for setting read lock/** * Creates bin with initial set of nodes headed by B.
            */Treebin (treenode<k,v> b) {super (treebin, NULL, NULL, NULL);
            This.first = b;
            Treenode<k,v> r = null;
                for (treenode<k,v> x = B, next; x!= null; x = next) {next = (treenode<k,v>) x.next;
                X.left = X.right = null;
                    if (r = = null) {x.parent = null;
                    X.red = false; R =X
                    else {k k = X.key;
                    int h = X.hash;
                    class<?> KC = null; for (treenode<k,v> p = r;;)
                        {int dir, ph;
                        K pk = P.key;
                        if (ph = P.hash) > h) dir =-1;
                        else if (ph < h) dir = 1;
                                 else if (KC = null && (KC = comparableclassfor (k)) = null) | |
                            (dir = Comparecomparables (KC, K, pk)) = = 0) dir = tiebreakorder (k, PK);
                        treenode<k,v> XP = P;
                            if (p = (dir <= 0) P.left:p.right = = null) {x.parent = XP;
          if (dir <= 0) xp.left = x;                  else xp.right = x;
                            R = balanceinsertion (r, X);
                        Break
            }}} This.root = R;
        Assert checkinvariants (root); }//........other Methods}
5, Forwardingnode: Inserts a node into a bucket in the transfer operation
    /* A node inserted at head of bins during transfer operations. * In the transfer operation, a node is inserted into the bins * * Static final class Forwardingnode<k,v> extends node<k,v> {FINA
        L node<k,v>[] nexttable;
            Forwardingnode (node<k,v>[] tab) {//node (int hash, K key, V Val, node<k,v> next) is the constructor of the Node class
            Super (moved, NULL, NULL, NULL);
        this.nexttable = tab; node<k,v> Find (int h, Object K) {//loop to avoid arbitrarily deep recursion on forwarding n Odes Outer:for (node<k,v>[] tab = nexttable;;)
                {node<k,v> e; int n; if (k = null | | | tab = NULL | |
                    (n = tab.length) = = 0 | |
                (E = tabat (tab, (n-1) & h)) = = null) return null; for (;;) {int eh;
                    K Ek; if (eh = e.hash) = = h && ((ek = e.key) = = k ||
                        (Ek!= null && k.equals (EK)))
                    return e; if (Eh < 0) {if (e instanceof forwardingnode) {tab = (forwarding
                            node<k,v>) e). nexttable;
                        Continue outer;
                    else return E.find (H, K);
                } if ((e = e.next) = = null) return null; }
            }
        }
    }
The principle analysis of the put (K key, V value) method in the Concurrenthashmap class

After we have a little understanding of node, TreeNode and Treebin, we can see how the put method of the Concurrenthashmap class is implemented, and here's a suggestion that the most we use for the container is the put and get method, We look at the implementation of the source code, our core concern is put, the implementation of Get method, as long as we understand the implementation of these two methods, this kind of the general realization of thought we will know ha

Based on this, let's look at the put method of the Concurrenthashmap class first.

The function of the put (K key, V value) method: Mapping the set of key-value pairs into a table, Key/value cannot be null

The code for the Put method is as follows:

    Public V-Put (K key, v. value) {return
        putval (key, value, false);
    }

Since it is directly called the Putval (key, value, False) method, we continue to look at it.

The code for the Putval (key, value, False) method is as follows:

    /** implementation for put and Putifabsent/final V Putval (k key, V value, Boolean onlyifabsent) {if (k EY = = NULL | |
        Value = = null) throw new NullPointerException ();
        int hash = spread (Key.hashcode ());//compute hash value, two hash operation int bincount = 0; for (node<k,v>[) tab = table;;)
            {//similar to while (true), dead loop until insert succeeded node<k,v> F; int n, I, FH; if (tab = = NULL | |
                (n = tab.length) = = 0)//Check whether initialization, if not, initialize tab = Inittable ();
                    /* i= (n-1) &hash is equivalent to i=hash%n (provided that n is a power exponent of 2). The node that pulls the position in the table is represented by F. There are two kinds of cases: 1, if the table[i]==null (that is, the location of the node is empty, no collision), then use the CAS operation directly stored in the location, if the CAS operation succeeds then exit dead
                    Ring. 2, if Table[i]!=null (that is, the location has other nodes, collision)/else if (f = tabat (tab, i = (n-1) & hash) = = N ull) {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)//check table[i] The hash of the node equals move D, if it is equal to, if the expansion is detected, to help its expansion tab = Helptransfer (tab, f);//help its expansion else {//Run here, description Table[i] The hash value of the node is not equal to
                Moved.
                V oldval = null;
                        Synchronized (f) {//Lock, (hash value of the same linked list of the head node) if (Tabat (tab, i) = = f) {//Avoid multiple threads, need to be checked again
                            if (FH >= 0) {//link table node Bincount = 1; 
                            /* The following code is to find the list of whether this key, if it appears, then update value, and jump out of the loop, otherwise the node will be added to the end of the Ernie and out of circulation
                                * 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)//Only onlyifabsent is true in Putifabsent () method

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.