Java Concurrent Framework Concurrent Container CONCURRENTHASHMAP (jdk5,6,7,8) source code Analysis &concurrentskiplistmap__java

Source: Internet
Author: User
Concurrenthashmap

Concurrenthashmap is a thread-safe hash Table, and its main function is to provide a set of methods that are the same as Hashtable functionality but thread-safe. Concurrenthashmap can read data without locking, and its internal structure allows it in the write operation can keep the size of the lock as small as possible, do not have to lock the entire concurrenthashmap. the internal structure of the Concurrenthashmap

Concurrenthashmap in order to improve its concurrency capability, in the internal use of a structure called segment, a segment is actually a class hash table structure, segment internal maintenance of a list of linked lists, Let's look at the internal structure of the Concurrenthashmap with the following picture:

From the structure above, we can see that the process of locating an element requires two hash operations, the first hash to the segment, and the second hash to the head of the linked list where the element is located, so the Concurrenthashmap The side effect of this kind of structure is that the process of the hash is longer than the ordinary hashmap, but the benefit is that the writing operation can only add the lock to the segment of the element, and will not affect the other segment, so that, in the ideal case, Concurrenthashmap can support a segment number of write operations at the same time (just as these writes are evenly distributed across all segment), so the concurrency capability of Concurrenthashmap can be greatly improved through this structure. Segment

Let's take a concrete look at the data structure of segment:

1 Static finalclasssegment<k,v> extendsreentrantlockimplements Serializable {
2 Transientvolatileint count;
3 Transientintmodcount;
4 Transientintthreshold;
5 Transientvolatilehashentry<k,v>[] table;
6 Finalfloatloadfactor;
7 }</k,v></k,v>

Explain in detail the meaning of the member variables in the segment: the number of elements in the count:segment Modcount: The number of operations that affect the size of the table (such as put or remove operations) threshold: thresholds, The number of elements inside the segment is still expanding to segment. Table: list array, each element in the array represents the head of a linked list loadfactor: Load factor, used to determine threshold hashentry

The elements in the segment are stored as hashentry in the array of linked lists, and look at the structure of the Hashentry:

1 Static finalclasshashentry<k,v> {
2 Finalk key;
3 Finalinthash;
4 Volatilev value;
5 Finalhashentry<k,v> Next;
6 }</k,v></k,v>

You can see one of the characteristics of hashentry, in addition to value, several other variables are final, this is to prevent the chain list structure is broken, the occurrence of concurrentmodification. Initialization of Concurrenthashmap

Here we combine the source code to specifically analyze the implementation of CONCURRENTHASHMAP, first look at the initialization method:

01 Public Concurrenthashmap (Intinitialcapacity,
02 Floatloadfactor,int concurrencylevel) {
03 if (!) ( Loadfactor >0) | | Initialcapacity <0| | Concurrencylevel <= 0)
04 Thrownewillegalargumentexception ();
05
06 if (Concurrencylevel > Max_segments)
07 Concurrencylevel = max_segments;
08
09 Find Power-of-two sizes Best matching arguments
10 Intsshift = 0;
11 Intssize = 1;
12 while (Ssize < Concurrencylevel) {
13 ++sshift;
14 Ssize <<=1;
15 }
16 Segmentshift =32-sshift;
17 Segmentmask = ssize-1;
18 this.segments = Segment.newarray (ssize);
19
20 if (Initialcapacity > Maximum_capacity)
21st initialcapacity = maximum_capacity;
22 INTC = initialcapacity/ssize;
23 if (c * Ssize < initialcapacity)
24 ++c;
25 Intcap = 1;
26 while (Cap < c)
27 Caps <<=1;
28
29 for (inti = 0; I <this.segments.length; ++i)
30 This.segments[i] =newsegment<k,v> (Cap, loadfactor);
31 }</k,v>

Currenthashmap initialization A total of three parameters, a initialcapacity, representing the initial capacity, a loadfactor, representing the load parameters, the last one is Concurrentlevel, Represents the number of segment within the CONCURRENTHASHMAP, Concurrentlevel once specified, cannot be changed, and subsequent increases in the number of CONCURRENTHASHMAP elements cause Conrruenthashmap to require expansion, Concurrenthashmap does not increase the number of segment, but only increases the capacity of the array of linked lists in segment, the advantage of which is that the expansion process does not need to rehash the entire concurrenthashmap, And you just need to do a rehash on the elements inside the segment.

The entire Concurrenthashmap initialization method is very simple, first based on the Concurrentlevel to new segment, where the number of segment is not more than the largest 2 of the index of Concurrentlevel, That is to say, the number of segment is always 2 of the number, the advantage is to facilitate the use of shift operations to hash, speeding up the process of hashing. The next step is to determine the size of the segment capacity based on intialcapacity, and the size of each segment is also 2 of the index, which also makes the hash process faster.

This side needs to pay special attention to two variables, respectively, Segmentshift and Segmentmask, these two variables will play a large role, assuming that the constructor determines the number of segment is 2 of the n-th, then segmentshift equals 32 minus N, And Segmentmask is equal to 2 of n times to minus one. Concurrenthashmap Get Operation

As mentioned earlier, the Concurrenthashmap get operation is not unlocked, let's look at its implementation here:

1 Public V get (Object key) {
2 Inthash = hash (Key.hashcode ());
3 Returnsegmentfor (hash). Get (key, hash);
4 }

Look at the third line, segmentfor This function is used to determine in which segment the operation should be performed, almost all operations on Concurrenthashmap need to use this function, we look at the implementation of this function:

1 Final segment<k,v> segmentfor (inthash) {
2 returnsegments[(hash >>> segmentshift) & Segmentmask];
3 }</k,v>

This function uses a bitwise operation to determine the segment, according to the incoming hash value to the right unsigned segmentshift bit, and then and segmentmask with the operation, combined with the segmentshift and Segmentmask values we said before, We can draw the following conclusions: Assuming that the number of segment is 2 n times, according to the elements of the hash value of the high N-bit can determine the element in exactly which segment.

After determining which segment to work with, the next thing to do is call the corresponding segment get method:

01 V Get (Object key, Inthash) {
02 if (count!=0) {//Read-volatile
03 Hashentry<k,v> e = GetFirst (hash);
04 while (e!=null) {
05 if (E.hash = = Hash && key.equals (E.key)) {
06 V v = e.value;
07 if (v!=null)
08 RETURNV;
09 Returnreadvalueunderlock (e);//Recheck
10 }
11 e = E.next;
12 }
13 }
14 Returnnull;
15 }</k,v>

First look at the second line of code, where count is judged, where count represents the number of elements in segment, we can look at the definition of Count:

1 transient volatile int count;

You can see that count is volatile, and in fact it uses the semantics of volatile: The write operation on the volatile field happens-before to each subsequent read operation of the same field.

Because in fact the put, remove and other operations will also update the value of count, so when the competition occurs, the semantics of volatile can ensure that the write operation in the read operation, but also ensure that the subsequent read operations are visible, This enables subsequent operations of the back get to obtain the full element content.

Then, on the third line, call GetFirst () to get the head of the list:

1 Hashentry<k,v> GetFirst (Inthash) {
2 hashentry<k,v>[] tab = table;
3 Returntab[hash & (Tab.length-1)];
4 }</k,v></k,v>

Similarly, here is also the use of bit operations to determine the head of the linked list, hash value and hashtable length minus one and operation, the final result is the hash value of the low n, where n is hashtable length of 2 as the end result.

After determining the head of the linked list, you can iterate through the entire list, look at line 4th, take out the value of the key corresponding to, if the value of values is NULL, this key,value may be in the process of put, if this happens, The lock is then added to ensure that the value being removed is complete and, if not NULL, returns value directly. Concurrenthashmap's put operation

After reading the get operation, and then look at the put operation, put the front is also determine the segment process, here no longer repeat, directly to see the key segment put method:

01 V Put (K key, Inthash, v Value,boolean onlyifabsent) {
02 Lock ();
03 try{
04 INTC = count;
05 if (c + + > Threshold)//ensure capacity
06 Rehash ();
07 hashentry<k,v>[] tab = table;

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.