In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
JavaThreadLocal usage example analysis, in view of this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.
ThreadLocal implements thread local variables in Java. The so-called thread local variable is some unique data stored in each thread, we know that all threads in a process share the resources of the process, and the changes made by the thread to the resources in the process will be reflected on other threads in the process. If we hope that the changes made by one thread to the resources will not affect other threads, then we need to set the resource in the form of thread local variables.
Basic use of ThreadLocal
As shown in the following example, define two ThreadLocal variables, then modify the thread local variables in the main thread and the child thread, and then get the values of the thread local variables respectively:
Public class ThreadLocalTest {private static ThreadLocal threadLocal1 = ThreadLocal.withInitial (()-> "threadLocal1 first value"); private static ThreadLocal threadLocal2 = ThreadLocal.withInitial (()-> "threadLocal2 first value"); public static void main (String [] args) throws Exception {Thread thread = new Thread (()-> {System.out.println ("=" + Thread.currentThread (). GetName () + "enter=")
/ / print out the initial value printThreadLocalInfo () in the child thread
/ / set the new value threadLocal1.set ("new thread threadLocal1 value"); threadLocal2.set ("new thread threadLocal2 value") in the child thread
/ / the child thread prints out the new value printThreadLocalInfo (); System.out.println ("=" + Thread.currentThread (). GetName () + "exit=");}); thread.start ()
/ / wait for the new thread to execute thread.join ()
/ / print threadLocal1 and threadLocal2 in the mainthread to verify whether the child thread's modifications to these two variables affect the two values printThreadLocalInfo () in the main thread.
/ / set the new values threadLocal1.set ("main threadLocal1 value") and threadLocal2.set ("main threadLocal2 value") for threadLocal1 and threadLocal2 in the main thread
/ / verify whether the two variables in the mainthread are new values printThreadLocalInfo ();} private static void printThreadLocalInfo () {System.out.println (Thread.currentThread (). GetName () + ":" + threadLocal1.get ()); System.out.println (Thread.currentThread (). GetName () + ":" + threadLocal2.get ());}}
The running results are as follows:
= Thread-0 enter=Thread-0: threadLocal1 first valueThread-0: threadLocal2 first valueThread-0: new thread threadLocal1 valueThread-0: new thread threadLocal2 value=Thread-0 exit=main: threadLocal1 first valuemain: threadLocal2 first valuemain: main threadLocal1 valuemain: main threadLocal2 value
If the child thread's modifications to threadLocal1 and threadLocal2 affect the threadLocal1 and threadLocal2 in the main thread, then printThreadLocalInfo () for the first time in the mainthread The new values should be printed out, that is, new thread threadLocal1 value and new thread threadLocal2 value, but the actual print result is not so, indicating that the changes to threadLocal1 and threadLocal2 in the new thread will not affect these two variables in the main thread. It seems that the threadLocal1 and threadLocal2 scope in the main thread is limited to the main thread, and the threadLocal1 and threadLocal2 scope in the new thread is limited to the new thread, which is the origin of thread local variables.
Principle of ThreadLocal implementation
As shown in the following figure, each thread object holds a threadLocals member variable of type java.lang.ThreadLocal.ThreadLocalMap, while ThreadLocalMap has a table member of type java.lang.ThreadLocal.ThreadLocalMap.Entry [], which is an array, the array element is of type Entry, the Entry is equivalent to a key and value,key pointing to the java.lang.ThreadLocal object shared by all threads, and the value points to the private variables of each thread, thus ensuring the isolation of thread local variables. Each thread simply reads and modifies the value object it holds without affecting each other.
Source code analysis (based on openjdk11)
The source code includes ThreadLocal and ThreadLocalMap,ThreadLocalMap is a static inner class defined within ThreadLocal for storing actual data. It is possible to create a threadLocals member (of type ThreadLocalMap) of the current thread when calling either the get or the set method of ThreadLocal.
Get method:
The get method for ThreadLocal is defined as follows
/ * * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@ link # initialValue} method. * * @ return the current thread's value of this thread-local * / public T get () {
/ / get the current thread Thread t = Thread.currentThread ()
/ / gets the threadLocals member variable of the current thread, which is a ThreadLocalMap ThreadLocalMap map = getMap (t)
/ / if threadLocals is not null, ThreadLocal is directly extracted from threadLocals.
/ / the value corresponding to the object if (map! = null) {
/ / get the Entry object corresponding to the current ThreadLocal object ThreadLocalMap.Entry e = map.getEntry (this) from map
If (e! = null) {
/ / get the value corresponding to the ThreadLocal object @ SuppressWarnings ("unchecked") T result = (T) e.value; return result;}}
/ / if threadLocals is null, you need to create a ThreadLocalMap object and assign it to the
/ / threadLocals, call initialValue with the current ThreadLocal object as key
/ / the initial value obtained is placed in the entry of threadLocals as value
/ / or threadLocals is not null, but not in threadLocals
/ / if you find the entry corresponding to the current ThreadLocal object, you need to add a new one to the threadLocals
/ / entry, which uses the current ThreadLocal object as the key and calls initialValue
/ / the value obtained as value return setInitialValue ();}
/ * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @ param t the current thread * @ return the map * / ThreadLocalMap getMap (Thread t) {return t.threadLocals;}
When the threadLocals of Thread is null, or the corresponding entry of the current ThreadLocal object is not found in the threadLocals of Thread, enter the setInitialValue method; otherwise, enter the getEntry method of ThreadLocalMap.
SetInitialValue method
The definition is as follows:
Private T setInitialValue () {/ / gets the initial value. If we implement the initialValue method of ThreadLocal / / when defining the ThreadLocal object, we will call our custom method to get the initial value, otherwise / / use the default implementation of initialValue to return the null value T value = initialValue (); Thread t = Thread.currentThread (); / / get the threadLocals member of the current thread ThreadLocalMap map = getMap (t) If (map! = null) {/ / if threadLocals exists, set the value corresponding to the ThreadLocal object to the initial value map.set (this, value);} else {/ / otherwise create the threadLocals object and set the initial value createMap (t, value);} if (this instanceof TerminatingThreadLocal) {TerminatingThreadLocal.register (TerminatingThreadLocal) this);} return value;}
CreateMap method implementation
/ * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @ param t the current thread * @ param firstValue value for the initial entry of the map
* / void createMap (Thread t, T firstValue) {
/ / create a ThreadLocalMap object with the current ThreadLocal object and the initial value value
/ / construct the first entry of table in ThreadLocalMap. ThreadLocalMap object assignment
/ / threadLocals member t.threadLocals = new ThreadLocalMap (this, firstValue) to the thread;}
The construction method of ThreadLocalMap is defined as follows:
/ * * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. * / ThreadLocalMap (ThreadLocal firstKey, Object firstValue) {
/ / construct table array with the size of INITIAL_CAPACITY table = new entry [inner _ CAPACITY]
/ / calculate the index int I = firstKey.threadLocalHashCode & (INITIAL_CAPACITY-1) of key (ThreadLocal object) in table
/ use ThreadLocal object and value to construct entry object, and put it in the I position of table table [I] = new Entry (firstKey, firstValue); size = 1
/ / set the threshold of table. When the number of elements in table exceeds this threshold, you need to set the table
/ / resize. Usually resize setThreshold (INITIAL_CAPACITY) occurs when calling the set method of ThreadLocalMap;}
/ * Set the resize threshold to maintain at worst a 2 load factor. * / private void setThreshold (int len) {threshold = len * 2 / 3;}
Here firstKey.threadLocalHashCode is a hashcode defined in ThreadLocal, and the hashcode is used for hash operation to find the index of the entry corresponding to the ThreadLocal object in table.
GetEntry method
The definition is as follows:
/ * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @ param key the thread local object * @ return the entry associated with key, or null if no such * / private Entry getEntry (ThreadLocal key) {
/ / calculate the position of the ThreadLocal object in the table based on the hashcode of ThreadLocal int I = key.threadLocalHashCode & (table.length-1); Entry e = table [I]
/ / if e is null, there is no entry corresponding to key in table
/ / e.get ()! = key may be due to the hash conflict that causes the entry corresponding to key in table
/ /, you need to continue looking for if (e! = null & & e.get () = = key) return e; else
/ / e==null or e.get ()! = key continue to find the entry return getEntryAfterMiss (key, I, e) for key;}
The getEntryAfterMiss method is defined as follows:
/ * * Version of getEntry method for use when key is not found in * its direct hash slot. * * @ param key the thread local object * @ param i the table index for key's hash code * @ param e the entry at table [I] * @ return the entry associated with key, or null if no such * / private Entry getEntryAfterMiss (ThreadLocal key, int I, Entry e) {Entry [] tab = table; int len = tab.length
/ / keep looking back from position I of table until you find entry with key key (e! = null) {
ThreadLocal k = e.get ()
/ / if k==key, entry if (k==key) return e was found
/ / k = = null needs to delete the entry if (k = = null) expungeStaleEntry (I)
/ / k! = key & & k! = null continue to look back, nextIndex is to take (iTun1)
/ / that is, entry else i = nextIndex (I, len) of the first position in table; e = tab [I];} return null;}
The expungeStaleEntry method deletes the entry whose key is null, and then performs a rehash operation on the entry between the entry in the staleSlot location and the first entry after that. The purpose of rehash is to reduce the collision probability of the table:
/ * * Expunge a stale entry by rehashing any possibly colliding entries * lying between staleSlot and the next null slot. This also expunges * any other stale entries encountered before the trailing null. See * Knuth, Section 6.4 * * @ param staleSlot index of slot known to have null key * @ return the index of the next null slot after staleSlot * (all between staleSlot and this slot will have been checked * for expunging). * / private int expungeStaleEntry (int staleSlot) {Entry [] tab = table; int len = tab.length
/ / expunge entry at staleSlot
/ / Delete entry tab [staleSlot] .value = null; tab [staleSlot] = null in staleSlot location
/ / the number of elements in table minus one size--
/ / Rehash until we encounter null
/ / change the value between the entry at staleSlot and the next entry at null in table
/ / entry re-hash to a new location
/ / if the key of entry encountered is null, delete the entry Entry e; int i; for (I = nextIndex (staleSlot, len); (e = tab [I])! = null; I = nextIndex (I, len)) {
/ / e is the next entry ThreadLocal k = e.get (); if (k = = null) {
/ / if the key of entry is null, delete e.value = null; tab [I] = null; size--;} else {
/ / the key of entry is not null. You need to put entry in the new location int h = k.threadLocalHashCode & (len-1); if (h! = I) {tab [I] = null
/ / Unlike Knuth 6.4 Algorithm R, we must scan until
/ / null because multiple entries could have been stale.
/ / tab [h] conflict occurs if it is not null. Continue to find the next location while (tab [h]! = null) h = nextIndex (h, len); tab [h] = e;} return I;}
Set method
The set method of ThreadLocal is defined as follows:
/ * * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@ link # initialValue} * method to set the values of thread-locals. * * @ param value the value to be stored in the current thread's copy of * this thread-local. * / public void set (T value) {Thread t = Thread.currentThread ()
/ / get the threadLocals ThreadLocalMap map of the current thread = getMap (t)
/ / threadLocals does not directly set the new value for null if (map! = null) {map.set (this, value);} else {
/ / if threadLocals is null, you need to create a ThreadLocalMap object and assign it to the
/ / threadLocals member of Thread createMap (t, value);}}
CreateMap has analyzed it before, and then analyze the set method of ThreadLocalMap.
Set method of ThreadLocalMap
The set method of ThreadLocalMap is defined as follows: take the current ThreadLocal object as key, the passed value as the value, create an entry with key and value, and place it in the appropriate location in table:
/ * Set the value associated with key. * * @ param key the thread local object * @ param value the value to be set * / private void set (ThreadLocal key, Object value) {
/ / We don't use a fast path as with get () because it is at
/ / least as common to use set () to create new entries as
/ / it is to replace existing ones, in which case, a fast
/ / path would fail more often than not. Entry [] tab = table; int len = tab.length
/ / use key to calculate the position of entry in table int I = key.threadLocalHashCode & (len-1); / / tab [I] if it is not null, then a valid entry already exists in the first location, and you need to continue / / look for a new location for (Entry e = tab [I])
E! = null
E = tab [I = nextIndex (I, len)]) {
ThreadLocal k = e.get ()
/ / find the same entry as key, and update the value of value directly
If (k = = key) {e.value = value
Return;}
/ / when you encounter an entry whose key is null, delete the entry
If (k = = null) {replaceStaleEntry (key, value, I)
Return;}}
/ / at this time, the first location entry is null, and the new entry is placed in this location
Tab [I] = new Entry (key, value)
Int sz = + + size
/ / an attempt was made to clear invalid entry. If the purge fails and the number of valid entry in the table
/ / is greater than threshold, which performs rehash operation
If (! cleanSomeSlots (I, sz) & & sz > = threshold) rehash ()
}
ReplaceStaleEntry method
The function of replaceStaleEntry is to construct the entry with the key and value passed by the set method, and put the entry somewhere after the staleSlot:
/ * * Replace a stale entry encountered during a set operation * with an entry for the specified key. The value passed in * the value parameter is stored in the entry, whether or not * an entry already exists for the specified key. * As a side effect, this method expunges all stale entries in the * "run" containing the stale entry. (A run is a sequence of entries * between two null slots.) * * @ param key the key * @ param value the value to be associated with key * @ param staleSlot index of the first stale entry encountered while * searching for key * / private void replaceStaleEntry (ThreadLocal key, Object value
Int staleSlot) {Entry [] tab = table
Int len = tab.length
Entry e
/ / Back up to check for prior stale entry in current run.
/ / We clean out whole runs at a time to avoid continual
/ / incremental rehashing due to garbage collector freeing
/ / up refs in bunches (i.e., whenever the collector runs).
/ / find the location of the first entry whose key is null from staleSlot int slotToExpunge = staleSlot
For (int I = prevIndex (staleSlot, len)
(e = tab [I])! = null
I = prevIndex (I, len)) if (e.get () = = null) slotToExpunge = I
/ / Find either the key or trailing null slot of run, whichever
/ / occurs first
/ / look for for from the staleSlot location (int I = nextIndex (staleSlot, len)
(e = tab [I])! = null; I = nextIndex (I, len)) {ThreadLocal k = e.get ()
/ / If we find key, then we need to swap it
/ / with the stale entry to maintain hash table order.
/ / The newly stale slot, or any other stale slot
/ / encountered above it, can then be sent to expungeStaleEntry
/ / to remove or rehash all of the other entries in run. / / if k is the same as key, directly update value if (k = = key) {e.value = value;// to place the entry of the original staleSlot location to the I position, and the key of entry at the tab [I] is null tab [I] = tab [staleSlot]; tab [staleSlot] = e
/ / Start expunge at preceding stale entry if it exists
/ / No entry if with null key was found from staleSlot (slotToExpunge = = staleSlot)
The key of entry at / / tab [I] is null, that is, the key of entry at tab [slotToExpunge] is null slotToExpunge = I
/ / clear entry at slotToExpunge location and perform rehash operation. CleanSomeSlots (expungeStaleEntry (slotToExpunge), len); return;}
/ / If we didn't find stale entry on backward scan, the
/ / first stale entry seen while scanning for key is the
/ / first still present in the run.
If (k = = null & & slotToExpunge = = staleSlot)
SlotToExpunge = I;}
/ / If key not found, put new entry in stale slot tab [staleSlot] .value = null
Tab [staleSlot] = new Entry (key, value)
/ / If there are any other stale entries in run, expunge them
If (slotToExpunge! = staleSlot)
CleanSomeSlots (expungeStaleEntry (slotToExpunge), len);}
The following source code can only be understood, not in words. No more explanation.
CleanSomeSlots method
CleanSomeSlots method:
/ * Heuristically scan some cells looking for stale entries.
* This is invoked when either a new element is added, or
* another stale one has been expunged. It performs a
* logarithmic number of scans, as a balance between no
* scanning (fast but retains garbage) and a number of scans
* proportional to number of elements, that would find all
* garbage but would cause some insertions to take O (n) time.
* * @ param i a position known NOT to hold a stale entry. The
* scan starts at the element after i.
* * @ param n scan control: {@ code log2 (n)} cells are scanned
* unless a stale entry is found, in which case
* {@ code log2 (table.length)-1} additional cells are scanned.
* When called from insertions, this parameter is the number
* of elements, but when from replaceStaleEntry, it is the
* table length. (Note: all this could be changed to be either
* more or less aggressive by weighting n instead of just
* using straight log n. But this version is simple, fast, and
* seems to work well.)
* * @ return true if any stale entries have been removed.
* / private boolean cleanSomeSlots (int I, int n) {boolean removed = false
Entry [] tab = table
Int len = tab.length
Do {I = nextIndex (I, len)
Entry e = tab [I]
If (e! = null & & e.get () = = null) {n = len
Removed = true
I = expungeStaleEntry (I)
}} while ((n > = 1)! = 0)
Return removed;}
Rehash method
Rehash method:
/ * Re-pack and/or re-size the table. First scan the entire * table removing stale entries. If this doesn't sufficiently * shrink the size of the table, double the table size. * / private void rehash () {expungeStaleEntries (); / / Use lower threshold for doubling to avoid hysteresis if (size > = threshold-threshold / 4) resize ();}
ExpungeStaleEntries method
ExpungeStaleEntries method:
/ * Expunge all stale entries in the table. , /
Private void expungeStaleEntries () {
Entry [] tab = table
Int len = tab.length
For (int j = 0; j < len; jacks +) {
Entry e = tab [j]
If (e! = null & & e.get () = = null)
ExpungeStaleEntry (j)
}
}
Resize method
Resize method:
/ * Double the capacity of the table. , /
Private void resize () {
Entry [] oldTab = table
Int oldLen = oldTab.length
Int newLen = oldLen * 2
Entry [] newTab = new Entry [newLen]
Int count = 0
For (Entry e: oldTab) {
If (e! = null) {
ThreadLocal k = e.get ()
If (k = = null) {
E.value = null
/ / Help the GC}
Else {int h = k.threadLocalHashCode & (newLen-1)
While (newTab [h]! = null) h = nextIndex (h, newLen)
NewTab [h] = e; count++
}}}
SetThreshold (newLen)
Size = count
Table = newTab
} adLocal
This is the end of the answer to the example analysis question on the usage of JavaThreadLocal. I hope the above content can be of some help to you. If you still have a lot of doubts to solve, you can follow the industry information channel for more related knowledge.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.