ConcurrentHashMap higher efficiency than other security concurrent collection of some?

Foreword

We know, ConcurrentHashmap (1.8) The concurrent collections framework is thread safe, when you get to see the source of the operation, you will find get full operation is not added any lock, which is the problem discussed in this blog - why it You do not need to lock it?

v2-7688019cecceae9c1e317f84267996d8_hd.png

Introduction ConcurrentHashMap

I want to have friends know basis in jdk1.7 is the use of Segment + HashEntry + ReentrantLock way to achieve, but gave up 1.8 Segment bloated design, replaced by the introduction Node + CAS + Synchronized to ensure the safe conduct concurrent realization .

  • JDK1.8 achieve reduction lock granularity, JDK1.7 version lock granularity is based on the Segment, comprising a plurality of HashEntry, the lock granularity is JDK1.8 HashEntry (head node)

  • JDK1.8 version of the data structure easier, so that the operation is more clear and smooth, because it has been used to synchronize synchronized, so that the concept does not need to segment the lock, there is no need Segment this data structure, because the particle size reduce implementation complexity also increases

  • JDK1.8 red-black tree to optimize the use of the list, based on the length of a long chain traversal is a very long process, and the efficiency of the red-black tree traversal is fast, instead of the list of predetermined threshold values, so that the best partner to form a


v2-0166b40bc89bf1282cf79a5fbd39c844_hd.jpg

get operation source

  • First calculate a hash value, to locate the position of the index table, if it is in line with the first node returns

  • If you have the time expansion, it will call signs node ForwardingNode the find method is expansion and look for the node, match returns

  • Above do not meet, then it is down to traverse the node, match returns, otherwise it returns null final

// find no source plus a lock 
public V GET (Object Key) { 
  the Node <K, V> [] Tab; the Node <K, V> E, P; n-int, EH; K EK; 
  int H = spread (key.hashCode ()); // calculate the hash 
  ! IF ((Tab = Table) = null && (= n-tab.length)> 0 && 
   (E = tabAt (Tab, (n--. 1) & H) )! = null) {// read the first node node element 
    if ((eh = e.hash) == h) {// If the node is the first node returns 
      if ((ek = e.key) == || Key (= null && key.equals EK (EK)!)) 
        return e.val; 
    } 
    // the hash value is represented by a negative expansion, at this time the check is ForwardingNode to find methods to locate nextTable 
    // eh = -1, indicating that the node is a ForwardingNode, migrating at this time of the call ForwardingNode find ways to nextTable where to find. 
    // eh = -2, indicating that the node is a TreeBin, at this time the call TreeBin find red-black tree traversal method, since the red-black tree possible discoloration is rotating, so there will find read-write lock. 
    // eh> = 0, described in the linked node is a linked list, the linked list can be traversed directly.
    else if (eh <0)
      ! return (the p-e.find = (H, Key)) = null p.val:? null; 
    the while (! (E = e.next) = null) {// neither a head node is not ForwardingNode, then go traversing the 
      IF (H == e.hash && 
       ((EK = e.key) == || Key (= null && key.equals EK (EK)))!) 
         return e.val; 
    } 
  } 
  return null; 
}

get not locked, then, ConcurrentHashMap is how to ensure that data is not read dirty data in it?

volatile debut

For visibility, Java provides the volatile keyword to ensure visibility, orderly. But it does not guarantee atomicity.

Ordinary share variables can not guarantee the visibility, because after a normal shared variables are modified, and when is written to main memory is uncertain when the other threads to read at this time may still be the original value of the old memory, making it impossible ensure visibility.

  • The volatile keyword can be consistent in the subsequent reading of multiple threads of the basic types of changes, but for reference types such as arrays, entity bean, only to ensure the visibility of reference, but does not guarantee the visibility of reference content. .

  • Prohibition command reordering.

Background: In order to increase the processing speed, memory, and processor does not communicate directly, but first the system memory to read data internal cache (L1, L2, or other) before the operation, but the operation is not finished is written to know when RAM.

  • If you declare a variable volatile write operation is performed, JVM will send a command to the processor, where the variable data cache line is written back to system memory. However, even if written back to memory if the other processor cache or old values, then perform computing operations will be a problem.

  • In multi-processor, in order to ensure each processor cache is consistent, it will cache coherency protocol, when a CPU when writing data, if found operating variable is a shared variable, it will notify the other CPU to inform the variable cache line is invalid and therefore the other when the CPU reads the variable is found to be invalid reload the data from main memory.

v2-7635e01a703b2a5fb6aa9c8442063d47_hd.jpg

To conclude:

First: the use of volatile keyword will force the modified value is written immediately main memory;

Second: the use of the volatile keyword, then, when the thread 2 to modify, invalidate the cache line of the working memory of the thread 1 in the cache variable (reflected in the hardware layer, it is L1 or L2 cache of the CPU in the corresponding cache line is invalidated );

Will go to the main memory to read due to an invalid thread 1 working memory cache line cache variable, the value of the variable thread 1 read again: third.

Welcome to the concern I kind of public-ho [programmers] marks the beginning of the article will be updated on the inside, documentation will be on the inside.

It is added to the array of volatile?

/**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node<K,V>[] table;

We know that volatile array can be modified, but different meaning and it looks like on the surface. For chestnut, volatile int array [10] refers to the value of array addresses is not volatile and volatile elements of the array.

v2-c05e15b7cc5f08026960565f8b0c17fa_hd.png

With volatile modified Node

get no lock operation may be due to the val element and the next Node pointer is that volatile, node A new thread or modified val nodes in a multi-threaded environment when the thread B is visible.

static class Node<K,V> implements Map.Entry<K,V> {
  final int hash;
  final K key;
  //可以看到这些都用了volatile修饰
  volatile V val;
  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; }
  public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
  public final String toString(){ return key + "=" + val; }
  public final V setValue(V value) {
    throw new UnsupportedOperationException();
  }
  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.
  */
  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;
  }
}

Since the volatile modification to get an array operation has no effect What added to the array of volatile purpose is it?

In fact, just to make Node array with visibility and added to other threads when the volatile expansion


to sum up

  • ConcurrentHashMap in 1.8 get the whole operation does not require locking, this is it than the other concurrent collections such as hashtable, with Collections.synchronizedMap () packaging hashmap; one reason for the high security efficiency.

  • get the whole operation does not need to be locked because the members of Node val is volatile and modified arrays nothing to do with the volatile modification.

  • Array with volatile modification is mainly to ensure that when the expansion of the array to ensure visibility.

At last 

Welcome to share with everyone, like I remember the article I focus a point like yo, thanks for the support!


Guess you like

Origin blog.51cto.com/14442094/2435488