Threadlocal Source Analysis (JDK8)

Source: Internet
Author: User
Tags rehash static class
Threadlocal characteristics and use of the scene: 1, to facilitate the same thread to use an object, to avoid unnecessary parameter transfer; 2, data isolation between threads (each thread uses its own local variables in its own thread, the threadlocal objects between the threads do not affect each other); 3. Get a database connection, session, correlation ID (such as log UniqueID, easy to string multiple logs);
Threadlocal should note: 1, threadlocal does not solve the problem of multi-threaded access to shared objects; 2, threadlocal not each thread copy an object, but directly new (new) one; 3, if Threadlocal.set () object is multithreaded, it still involves concurrency issues. 1. Initialization of threadlocal<t>Private final int threadlocalhashcode = Nexthashcode (); private static final int hash_increment= 0x61c88647; /** * The next hash code to is given out. Updated atomically. Starts at zero. *///source code said Nexthashcode initial value of 0, but the actual debugging display initial value of 1253254570, inexplicable. And when the initialization is complete, the value of Nexthashcode becomes 0, indicating that its initial value is 0.
private static Atomicinteger Nexthashcode = new Atomicinteger ();
private static Intnexthashcode () {return
    nexthashcode.getandadd (hash_increment);
}
There are 3 threadlocal class variables, 2 of which are static variables (including a constant), and the actual variable as a threadlocal instance is only threadlocalhashcode these 1, and the initialization is immutable. What do you do when you create a threadlocal instance: threadlocal initializes the Nexthashcode () method to initialize the Threadlocalhashcode, and the Threadlocalhashcode is not changed after initialization. Threadlocalhashcode can be used to mark different instances of threadlocal.
2. Internal class2.1 Threadlocalmap Threadlocalmap is a custom hashmap that maintains the local variable values for the current thread only. Only the Threadlocal class has permission to operate on it and is the private property of thread. To avoid large footprint or long life cycle data that resides in memory raises a series of problems, the hash table key is a weak reference weakreferences. When there is not enough space, the entry is cleaned up. Key points in the Threadlocalmap:
Static class Entryextends weakreference<threadlocal<?>>{
   /** The value associated with this ThreadLocal . *
    /Object value;
     Entry (threadlocal<?> K, Object v) {
       super (k);
        Value= v
   }
}
Note:The key of Threadlocalmap is Threadlocal,value is object (what we call "thread Local Data").
2.2 Suppliedthreadlocal<t> extends Threadlocal<t> suppliedthreadlocal is the new inner class for JDK8. Only the method that expands the initialization value of the threadlocal allows the new lambda expression to be assigned using JDK8. It should be noted that the functional interface supplier is not allowed to be null. The source code is as follows:
Static final class Suppliedthreadlocal<t>extends threadlocal<t>{
    private final Supplier<?extends T > supplier;
     Suppliedthreadlocal (supplier<?extends t> Supplier) {
       this.supplier= objects.requirenonnull (Supplier);
   }
     @Override
   protected T initialvalue () {return
       supplier.get ();
   }
}
3. Main methods3.1, T get () returns the value of the current thread.
Public T gets () {thread
    t= thread.currentthread ();//Get current thread
    threadlocalmap map= getmap (T);//get current thread map
   if (map!=null) {
        Threadlocalmap.entry e = Map.getentry (this);//See 3.1.1
       if (e!=null) {//map is not empty and the current thread has value. Returns
            the value @SuppressWarnings ("unchecked")
            T result= (t) e.value;
           return result;
       }
   return Setinitialvalue (); Initialize and return value
}
-----Getmap's source code:
Threadlocalmap Getmap (Thread t) {
   returnt.threadlocals;
}
Getmap (t) returns the member variable of the current thread Threadlocalmap (thread's member variable has threadlocalmap, which can view thread's source code, as follows) it is clear that threadlocal belongs to the thread, Threadlocalmap is held by Threadlocal, and in the final analysis, Threadlocalmap is also held by threads. Thread threads each have their own threadlocalmap. /* ThreadLocal values pertaining to this thread. This map was maintained by the ThreadLocal class. * * Threadlocal.threadlocalmap threadlocals = null;
--------Setinitialvalue Source:
Private T Setinitialvalue () {
    T value= initialvalue ();//Call overridden InitialValue, return new value
    Thread t= thread.currentthread ();
    Threadlocalmap map= Getmap (t);
   if (map!=null)//When the threadlocalmap of the front thread is not empty, the direct assignment
        Map.set (this, value);
   else
//Create a Threadlocalmap (this, firstvalue) for the current thread and assign an initial value, this is the current thread
        createmap (t, value);
   return value;
}
Protected T InitialValue () {
    return t;//Custom returned value
};
Createmap Source:
void Createmap (Thread T, T firstvalue) {
    t.threadlocals=new threadlocalmap (this, firstvalue);
threadlocal Get process:1. Get the current thread T; 2, the member variable Threadlocalmap that returns the current thread T (the following shorthand map); 3, the map is not NULL, gets the entry of the Threadlocalmap with the current thread as key (hereinafter abbreviated E), if E is not null , the value of the entry is returned directly, 4 if the map is null or the E is null, and Setinitialvalue () is returned. Setinitialvalue () calls the overridden InitialValue () to return the new value (if no Override InitialValue will return the default value null), The new value is saved to the current thread's Threadlocalmap (if the current thread does not have a threadlocalmap, it is created first).
3.2. Void set (T value) is assigned the current threadlocal (initial value or new value) for the current thread. Quite similar to Setinitialvalue, there is no more analysis.
public void Set (T value) {
    Thread t= thread.currentthread ();
    Threadlocalmap map= Getmap (t);
   if (map!=null)
        Map.set (this, value);
   else
        createmap (t, value);
3.3. void remove () to get the current thread's threadlocalmap,map is not empty, remove the current threadlocal as key value pairs.
public void Remove () {
     threadlocalmap m= getmap (Thread.CurrentThread ());
    if (m!=null)
         m.remove (this);
 }
Note:Remove () removes the current threadlocal data for the current thread (just empties the Key-value key value pair), is removed immediately, and then calls the Get method to invoke the InitialValue method initialization (unless the Set method assignment is called during this period).
3.4. Static <S> threadlocal<s> withinitial (supplier<? extends s> Supplier) JDK8 New, support lambda expressions, and Threadlocal overrides the InitialValue () effect.
Public static<s> threadlocal<s> withinitial (supplier<?extends s> Supplier) {return
   new suppliedthreadlocal<> (supplier);
}
As you can see, the Withinitial () method's entry is a functional interface supplier, and the return value is the JDK8 new inner class suppliedthreadlocal, and as 2.2 says, the difference is only in support of the lambda expression assignment. The use case is as follows:
@Test public
void Jdk8test () {
    supplier<string> Supplier =new supplier<string> () {
         @Override Public
       String Get () {return
           ' supplier_new ';
       }
   };
    Threadlocal= threadlocal.withinitial (supplier);
    System.out.println (Threadlocal.get ());//Supplier_new
    threadlocal= threadlocal.withinitial (()-> "sup_new_2 ");
    System.out.println (Threadlocal.get ());//sup_new_2
    threadlocal<dateformat> localdate = Threadlocal.withinitial (()->new SimpleDateFormat ("Yyyy-mm-dd"));
    System.out.println (Localdate.get (). Format (new Date ());//2017-01-22
    threadlocal<string> Local =new Threadlocal<> (). withinitial (supplier);
    System.out.println (Local.get ());//Supplier_new
}
Note:withinitial (supplier) is a return value threadlocal, so it is assigned to the Threadlocal instance when it is instantiated. 4. Graphic ThreadLocalEach thread may have multiple threadlocal, and each threadlocal of the same line is stored in the same threadlocalmap.
Illustrated ThreadLocal (JDK8). VSDX Download Address: https://github.com/zxiaofan/JDK-Study/tree/master/src/java1/lang/threadLocal
5, Threadlocal-threadlocalmap Source Analysis
5.1, Entry getentry (threadlocal<?> key) First look at the Get method, you will find that the Threadlocalmap gets method is different from the traditional map, it returns not Key-value value, But the whole entry, at that time entry key is Threadlocal,value is stored value, this is consistent. A, Getentry source analysis:
Private Entry Getentry (threadlocal<?> key) {
   int i= key.threadlocalhashcode& (table.length-1);
    Entry e= Table[i];
   if (e!=null&& e.get () = = key) return
       E;
   else return
       Getentryaftermiss (key, I, E);
}
The Getenrty method will only handle the entry where the key is hit directly, and no direct hit (key conflicting) data will invoke the Getentryaftermiss () method to return the corresponding Enrty, as explained in the source code, in order to maximize the performance of the direct hit. the process of Threadlocalmap Getentry:1, calculate index (length-1) & Key.hash of entry array. Similarities and differences between index calculations and HashMap: ① similarities: The calculations are the same, both (length-1) & Key.hash;length are both of the underlying structure (size, not the actual size). ② difference: HashMap (JDK8) The underlying data structure is a bit bucket + linked list/red-black tree, and threadlocalmap the underlying data structure is entry array; HashMap's key.hash is calculated native, XOR, or unsigned, (key = = NULL)? 0: (H = key.hashcode ()) ^ (h >>>), Threadlocalmap Key.hash is determined by Nexthashcode () when instantiated from Threadlocal. 2, get the corresponding index of the node entry 3, if the return node entry has a value and its key does not conflict (only 1 is entry return key equals the incoming key), then return the entry directly; 4, return entry null or key conflict, Then call Getentryaftermiss (threadlocal<?> key, int i, Entry e) method to return Entry.
B, Getentryaftermiss source analysis:Getentryaftermiss handles those keys that are not hit when getentry (value is null direct return NULL,SO more specifically hit and conflicting key). The incoming parameter is the current Threadlocal,key index in the array, and the key value pair corresponding to the index.
Private Entry Getentryaftermiss (threadlocal<?> key,int i, Entry e) {
    entry[] tab = table;
   int len= tab.length;
    while (e!=null) {
        threadlocal<?> k = E.get ();
       if (k== key) return
           E;
       if (k==null)
            expungestaleentry (i);
       else
            i= Nextindex (i, Len);
        E= tab[i];
   }
   return null;
}
the process of Threadlocalmap Getentryaftermiss:Analyze only the case that the entry is not empty, 1, Get entry key, 2, if the key is consistent (memory address = judgment), then return the entry 3, if the key is null, then call the Expungestaleentry method erase the entry; 4, In other cases, the next index position is obtained by the Nextindex method index 5, get the entry at the new index, and then cycle 2/3/4 until the key is positioned to return entry or null.
private static int Nextindex (int i,int len) {
   return (i+1< len) (i+1:0);//Add 1 to the index
}
C, expungestaleentry source analysis:As long as the key is null will be erased, so that the corresponding value is not referenced to facilitate recycling.
private int expungestaleentry (int staleslot) {entry[] tab = table;
    int len= tab.length; Expunge entry at Staleslot Tab[staleslot].value=null; Erases the value Tab[staleslot]=null at the current index;
    Erase key size--at current index;
   Rehash until we encounter null Entry e;
   int i; For (i= Nextindex (Staleslot, Len);//Calculate the next index (e= tab[i))!=null;//new index entry NOT null i= nextindex (i, Len
            ) {//computes the next index threadlocal<?> k = E.get ();///Get new key (ThreadLocal) if (k==null) {//key is null, reset empty again
            E.value=null;
            Tab[i]=null;
       size--;
                }else{int h= k.threadlocalhashcode& (len-1);//Calculate new index if (h!= i) {//index if not changed, there is no extra entry.
                Tab[i]=null; 
Unlike Knuth 6.4 algorithm R, we must scan until//null because multiple, entries could and have been.
               Sweep to the last non-empty position and place the value as the first entry at the collision. while (tab[h]!=null) h= NEXtindex (H, Len);
           Tab[h]= e;
}} return I; }

5.2, set (threadlocal<?> key, Object value)
private void Set (Threadlocal<?> key, Object value) {
    //We don ' t use a fast path as and get () because it is at
  //least as common to use Set () to create new entries as
   //It's to replace existing ones, in which case, a fast
  //path would fail more often than not.
    entry[] tab = table;
   int len= tab.length;
   int i= key.threadlocalhashcode& (len-1);
    For (Entry e= tab[i]; e!=null;e= tab[i= Nextindex (i, Len)]) {
//At the current index there is Entry threadlocal<?>
        k = E.get ();
        if (k== key) {//Key (ThreadLocal) is the same, update value
            e.value= value;
           return;
       }
        if (k==null) {//The Expired data//
traverse cleaning expired data and insert new data at index, other data is moved
            replacestaleentry (key, value, I);
           return;
       }
    Tab[i]=new Entry (key, value);
   int sz=++size;
No expired data is cleaned and the actual size exceeds the expansion threshold
   if (!cleansomeslots (i, SZ) && sz>= threshold)
        rehash ();
}

rehash ():The actual entry number of the size:table, the expansion threshold Threshold:table.lenrth (default 16) size of 2/3; First call expungestaleentries delete all expired data, if you clean up the data size>= Threshold 3/4, then twice times the expansion. PS: Threshold Yù value is also called a critical value, refers to an effect can produce the lowest or highest value. Valve Fá control, switch, hold.
Threadlocalmap and HashMap solution comparison in hash conflict:HASHMAP: If the conflict, the new data will be inserted logically into the linked list or red-black tree. See "HashMap Source Analysis (jdk1.8)" the logic of put (K key, V value):1, to determine whether the key value of the array tab[] is null or NULL, is the resize (), 2, based on the key value Hashcode () to calculate the hash value to get the index of the current node I ( Bucketindex, if the Tab[i]==null "no collision", add directly to the new node, otherwise "collision" into 3 3, to determine the current array of processing hash conflict in the form of red-black tree or linked list (check the first node type can), respectively. "① is a red-black tree is inserted by the red-black tree logic; ② is a linked list, then traverse the list to see if there are key nodes; ③ update value, no new node, if the number of linked list is greater than the valve value 8" 9 ", The Treeifybin method is invoked (this method first determines whether the table is null or tab.length is less than 64, then performs the resize operation, otherwise the linked list is changed to a red-black tree). "4, if size+1> threshold is resize."
Threadlocalmap:1, if the designated position index already has the data entry, each traversal entry:1.1, if the index key is same, then update value 1.2, if the index key is null, The call Replacestaleentry clears the expired data and inserts the new data (traversed from index until the same key update value is found, or has not been found, at index). Replacestaleentry traversal will be entry one after the move, that is, set in the latest entry will be placed in the index, convenient get when direct hit. 2, index no data, then put the new entry, then clean up the expiration data and determine whether twice times the expansion (Size>=threshold 3/4).
Reference: Http://www.cnblogs.com/dolphin0520/p/3920407.html have any questions, welcome to correct the discussion.
Welcome individual reprint, but must in the article page obvious position gives the original connection;
without the consent of the author must retain this paragraph of the statement, not arbitrarily modify the original, not for commercial purposes, otherwise reserves the right to pursue legal liability.

"CSDN": csdn.zxiaofan.com
"GitHub": github.zxiaofan.com

If you have any questions, please leave a message. Good luck to you. Life are all about
choices. 
in the future, you will be grateful for your hard work now.

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.