ConcurrentHashMap source code analysis

Source: Internet
Author: User

ConcurrentHashMap source code analysis
CocurrentHashMap uses HashTable to implement concurrent hash search and storage by locking the entire table. CocurrentHashMapt can implement the same functions through Segment, but it is more efficient. In jdk1.6, cocuentHashMap has a weak consistency problem, but this problem has been fixed at jdk1.7. Therefore, both concurrency security and performance are very high. Next I will try to analyze CocurrentHashMap Based on the jdk1.7 source code.
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 two);        SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);        TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);    }
Code parsing: first, obtaining Unsafe provides cas operations, and java underlying multi-thread concurrency is completed through cas. However, cas operations still have some problems for high-precision concurrency. [For this issue, we will analyze it later ]. UNSAFE. arrayBaseOffset (tc) and UNSAFE. arrayBaseOffset (SC) are both used to calculate the memory offset value of the HashEntry and Segment object to the array object. This is the value that must be obtained for the cas operation. Note:
// Get the offset of the first element in the array (get offset of a first element in the array) public native int arrayBaseOffset (java. lang. class aClass); // get the size of an element in the array (get size of an element in the array) public native int arrayIndexScale (java. lang. class aClass );


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
 
   s0 =            new Segment
  
   (loadFactor, (int)(cap * loadFactor),                             (HashEntry
   
    [])new HashEntry
    [cap]);        Segment
    
     [] ss = (Segment
     
      [])new Segment
      [ssize];        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]        this.segments = ss;    }
     
    
   
  
 

Code parsing: the above Code mainly serves to initialize the Segment [] array object and Segment object. Parse to analyze the most important put and get methods.
Put Method
    public V put(K key, V value) {        Segment
 
   s;        if (value == null)            throw new NullPointerException();        int hash = hash(key.hashCode());        int j = (hash >>> segmentShift) & segmentMask;        if ((s = (Segment
  
   )UNSAFE.getObject          // nonvolatile; recheck             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment            s = ensureSegment(j);        return s.put(key, hash, value, false);    }
  
 

Code parsing: Calculate the hash value of the key first, and then use this hash value to get the Segment object. Then the Segment object executes the put method. In this way, the put operation is completed. Since this process is very important, we certainly want to know how it handles concurrency and internal implementation.
EnsureSegment
    private Segment
 
   ensureSegment(int k) {        final Segment
  
   [] ss = this.segments;        long u = (k << SSHIFT) + SBASE; // raw offset        Segment
   
     seg;        if ((seg = (Segment
    
     )UNSAFE.getObjectVolatile(ss, u)) == null) {            Segment
     
       proto = ss[0]; // use segment 0 as prototype            int cap = proto.table.length;            float lf = proto.loadFactor;            int threshold = (int)(cap * lf);            HashEntry
      
       [] tab = (HashEntry
       
        [])new HashEntry
        [cap]; if ((seg = (Segment
        
         )UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck Segment
         
           s = new Segment
          
           (lf, threshold, tab); while ((seg = (Segment
           
            )UNSAFE.getObjectVolatile(ss, u)) == null) { if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) break; } } } return seg; }
           
          
         
        
       
      
     
    
   
  
 

First, the offset is calculated, and then the object is obtained using UnSafe. Here, you may be confused about how to obtain the offset value. Here, I also analyze the offset to obtain the value of long u = (k < Segment-> put
        final V put(K key, int hash, V value, boolean onlyIfAbsent) {            HashEntry
  
    node = tryLock() ? null :                scanAndLockForPut(key, hash, value);            V oldValue;            try {                HashEntry
   
    [] tab = table;                int index = (tab.length - 1) & hash;                HashEntry
    
      first = entryAt(tab, index);                for (HashEntry
     
       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
      
       (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;        }
      
     
    
   
  
 

Get ()
    public V get(Object key) {        Segment
  
    s; // manually integrate access methods to reduce overhead        HashEntry
   
    [] tab;        int h = hash(key.hashCode());        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;        if ((s = (Segment
    
     )UNSAFE.getObjectVolatile(segments, u)) != null &&            (tab = s.table) != null) {            for (HashEntry
     
       e = (HashEntry
      
       ) 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 very simple. First, locate the segment and then the object. GetObjectVolatie ensures that the latest data can be read.
Conclusion: concurrentHashMap involves a lot of multi-threaded knowledge and java Memory Model knowledge. If you do not have enough capabilities, do not imitate them, but we can learn its thoughts and how it is implemented.


 

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.