Concurrenthashmap Source Code Analysis

Source: Internet
Author: User

Cocurrenthashmap effectHashtable by locking the whole table to achieve concurrent hash lookup and storage, COCURRENTHASHMAPT through segment way can achieve the same function, but more efficient, At the time of jdk1.6, Cocuenthashmap had a weak consistency problem, but in jdk1.7, the problem was fixed. So concurrency security or performance is very high. Next I try to analyze cocurrenthashmap based on the source code of jdk1.7.
Cocurrenthashmap Initialization preprocessing
//unsafe mechanics private static final sun.misc.Unsafe unsafe;    Private static final long sbase;    private static final int sshift;    Private static final long tbase;    private static final int tshift;        static {int SS, TS;            try {UNSAFE = Sun.misc.Unsafe.getUnsafe ();            Class<?> TC = Hashentry[].class;            class<?> sc = segment[].class;            Tbase = Unsafe.arraybaseoffset (TC);            Sbase = Unsafe.arraybaseoffset (SC);            TS = Unsafe.arrayindexscale (TC);        SS = Unsafe.arrayindexscale (SC);        } catch (Exception e) {throw new Error (e); } if ((SS & (ss-1))! = 0 | |        (TS & (ts-1))! = 0) throw new Error ("Data type scale not a power of");        Sshift = 31-integer.numberofleadingzeros (ss);    Tshift = 31-integer.numberofleadingzeros (ts); }
Code parsing: First get the unsafe to provide CAS operations, Java bottom-line multithreading is done through the CAS, but the CAS operation for high-precision concurrency is still a certain problem. "As for this question, analyze it later." Both Unsafe.arraybaseoffset (TC) and Unsafe.arraybaseoffset (SC) are used to calculate the memory offset values of Hashentry and segment entity objects relative to the array object. This is the value that the CAS operation must get.notes:
Gets the offset (get offset of a first element in the array) of the initial elements in a  native int arraybaseoffset (Java.lang.Class aclass) ;  Gets the size of an element within the array (get size of an element in the array)  


Cocurrenthashmap Initialization
    Public Concurrenthashmap (int initialcapacity, float loadfactor, int concurrencylevel) { if (! ( Loadfactor > 0) | | Initialcapacity < 0 | |        Concurrencylevel <= 0) throw new IllegalArgumentException ();        if (Concurrencylevel > max_segments) concurrencylevel = max_segments;        Find Power-of-two sizes Best matching arguments int sshift = 0;        int ssize = 1;            while (Ssize < concurrencylevel) {++sshift;        Ssize <<= 1;        } this.segmentshift = 32-sshift;        This.segmentmask = ssize-1;        if (initialcapacity > maximum_capacity) initialcapacity = maximum_capacity;        int c = initialcapacity/ssize;        if (c * ssize < initialcapacity) ++c;        int cap = min_segment_table_capacity;        while (Cap < c) Cap <<= 1; Create segments and segments[0] segment<k,v> S0 =           New Segment<k,v> (Loadfactor, (int) (CAP * loadfactor), (hashentry<k,v>[]        ) New hashentry<?,? >[cap]);        segment<k,v>[] ss = (segment<k,v>[]) new segment<?,? >[ssize]; Unsafe.putorderedobject (SS, Sbase, S0);    Ordered write of segments[0] this.segments = SS; }

Code parsing: The main function of the above code is to initialize the segment[] Array object and the Segment object. Parse to analyze the most important put and get methods.
Put method
  <span style= "FONT-SIZE:18PX;" > Public  v put (K key, V value) {        segment<k,v> s;        if (value = = null)            throw new NullPointerException ();        int hash = hash (Key.hashcode ());        Int J = (hash >>> segmentshift) & Segmentmask;        if ((s = (segment<k,v>) unsafe.getobject          //nonvolatile; recheck             (segments, (J << Sshift) + sbase) = = NULL)//in  ensuresegment            s = ensuresegment (j);        Return S.put (key, hash, value, false);    } </span>

Code parsing: The general meaning is to calculate the hash value of key first, and then use this hash value to get the segment object. The segment object then executes the Put method. This completes the put operation. Because this process is very important, we definitely want to know how it handles concurrency and internal implementations.
ensuresegment
    Private segment<k,v> ensuresegment (int K) {Final segment<k,v>[] ss = this.segments; Long u = (k << sshift) + sbase;        Raw offset segment<k,v> seg;  if (seg = (segment<k,v>) unsafe.getobjectvolatile (ss, u)) = = null) {segment<k,v> proto = Ss[0];//            Use segment 0 as prototype int cap = Proto.table.length;            float lf = proto.loadfactor;            int threshold = (int) (CAP * lf);            hashentry<k,v>[] tab = (hashentry<k,v>[]) new hashentry<?,? >[cap]; if (seg = (segment<k,v>) unsafe.getobjectvolatile (ss, u)) = = null) {//Recheck Segmen                t<k,v> s = new segment<k,v> (LF, threshold, TAB); while (seg = (segment<k,v>) unsafe.getobjectvolatile (ss, u)) = = null) {if             (Unsafe.compareandswapobject (SS, U, NULL, SEG = s)) break;   }}} return seg; }

The offset is calculated first, and then the unsafe is used to get the object. Here it is possible to get a bit confused about this offset value, and here I also analyze this offset to get both long u= (K<<sshift) +sbase; It is possible for everyone to ask why this is calculated. The average person calculates the offset value: set K to index,size as the size of the object, _offset to the offset value of the first element, and the offset value should be offset=index*size+offset. Yes, theoretically, that's right. In the Java memory model, however, memory follows 8-byte alignment. So in the Java memory model you are wrong to calculate this way. There are previous initializations to know: Sshift the number of bits corresponding to the binary number of the object size. So the K<<sshift also achieves 8-byte alignment.
Segment->put
        Final V put (K key, int hash, V value, Boolean onlyifabsent) {hashentry<k,v> node = Trylock ()? n            Ull:scanandlockforput (key, hash, value);            V OldValue;                try {hashentry<k,v>[] tab = table;                int index = (tab.length-1) & hash;                hashentry<k,v> first = Entryat (tab, index); for (hashentry<k,v> e = first;;)                        {if (E! = null) {k k;                            if (k = e.key) = = Key | | (E.hash = = Hash && key.equals (k)))                            {oldValue = E.value;                                if (!onlyifabsent) {e.value = value;                            ++modcount;                        } break;                    } e = E.next;            } else {            if (node! = null) Node.setnext (first);                        else node = new Hashentry<k,v> (hash, key, value, first);                        int c = count + 1;                        if (C > Threshold && tab.length < maximum_capacity) rehash (node);                        else Setentryat (tab, Index, node);                        ++modcount;                        Count = C;                        OldValue = null;                    Break            }}} finally {unlock ();        } return oldValue; }
Here's a flowchart:



get ()
    Public V get (Object key) {        segment<k,v> S;//Manually integrate access methods to reduce overhead        hashentry <k,v>[] tab;        int h = hash (Key.hashcode ());        Long U = (((H >>> segmentshift) & Segmentmask) << sshift) + sbase;        if ((s = (segment<k,v>) unsafe.getobjectvolatile (segments, u))! = null &&            (tab = s.table)! = null) {            for (hashentry<k,v> e = (hashentry<k,v>) unsafe.getobjectvolatile                     (tab, ((Long) ((tab.length-1) & h)) << Tshift) + tbase);                 E! = null; E = E.next) {                K K;                if (k = e.key) = = Key | | (E.hash = = h && key.equals (k)))                    return e.value;            }        }        return null;    }

The principle is simple to first locate the segment and then navigate to the entity. And the Getobjectvolatie guarantees the ability to read the latest data.
Summary: Concurrenthashmap implementation involves a lot of multi-threaded knowledge and Java memory model this knowledge, if not enough ability, mind not to imitate, but we can learn its ideas and how to achieve.


Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

Concurrenthashmap Source Code Analysis

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.