Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the source code knowledge of java ThreadLocal?

2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the relevant knowledge of "what is the source knowledge of java ThreadLocal?". In the operation of actual cases, many people will encounter such a dilemma. Then let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Brief introduction

ThreadLocal is a data structure of a storage object maintained by each thread, and threads do not affect each other to achieve thread closure. Generally, we access the object through the get/set method of the ThreadLocal object.

Source code analysis

The source code of the set method for ThreadLocal is as follows

Public void set (T value) {Thread t = Thread.currentThread (); ThreadLocalMap map = getMap (t); / / get the ThreadLocalMap object if (map! = null) map.set (this, value) based on the current thread; / / setelsecreateMap (t, value) if any; / / otherwise create ThreadLocalMap object} ThreadLocalMap getMap (Thread t) {return t.threadLocals;} void createMap (Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap (this, firstValue);}

Through the getMap method, you can see that the map we return is actually the threadLocals property of the Thread object. And this ThreadLocalMap is the structure used to store data.

ThreadLocalMap introduction

ThreadLocalMap is the core of ThreadLocal, defined in the inner class of the ThreadLocal class, and maintains an array of Enrty. ThreadLocal stores / fetches data by manipulating the Enrty array.

The Enrty array, as a hash table, hashes objects into this array through the open address method. HashMap, by contrast, hashes objects into an array through a linked list.

The open address method is to hash elements to the position in the array if there is a conflict, and then find the next hash position in the array with some rule, while in ThreadLocalMap, it uses linear detection to find the position that can be hashed back in turn.

Enery introduction

The Enery, which we call elements here, is the object unit maintained in the hash table.

/ / elements in the hash map table inherit WeakReference using their reference fields as keys (it is always a ThreadLocal object). / / Note that the null key (that is, entry.get () = = null) means that the key is no longer referenced, so the element can be deleted from the table. / / these elements are called "old elements" in the following code. / / these "old elements" are dirty objects, because existing references will not be cleaned up in the code by GC,// to avoid memory leaks. Set the references to null, then these objects will be cleaned up by GC later. / / in fact, the following code describes to a large extent how to clean up the reference to "old elements" static class Entry extends WeakReference k = e.get (); if (k = = key) {/ / key already exists, update e.value = value;return directly } if (k = = null) {/ / e is not null but k is null means that k is GC as a weak reference, the old data needs to be cleaned up / / I is the old data location, clean up that location and reasonably hash or replace value into the array / / according to key and then re-hash the elements after I, and by the way clean up other old elements near position I replaceStaleEntry (key, value, I); return }} / / traverses to an array position where null is assigned tab [I] = new Entry (key, value); int sz = + + size / / call cleanSomeSlots to try to find and clean up the old elements. If it is not found and the current capacity of the old elements exceeds the threshold, call rehashif (! cleanSomeSlots (I, sz) & & sz > = threshold) / / consider that the table space is insufficient at this time. After full traversal to clean up the old elements, it is determined that if the capacity is greater than the threshold value of 3x4, then expand the capacity and re-hash rehash ();}

ReplaceStaleEntry method

The replaceStaleEntry method is executed if an old element is encountered when we detect it linearly. This method does a lot of things, which can be summed up as we find the old elements at the staleSlot location, overwrite the new values to the staleSlot location, and clean up the old elements near the staleSlot. "nearby" refers to non-null elements that are consecutive before and after the staleSlot location. The code and detailed comments are as follows

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). / / check forward to see if there are old elements, clean up the old data caused by the weak reference key cleared by GC once and for all, and avoid executing int slotToExpunge = staleSlot multiple times. / / iterate forward to find the location where entry is not empty and key is null. Assign to slotToExpungefor (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// staleSlot position traversing backwards if the location is not empty, determine whether key already exists / / recall that this method was called when we encountered old elements when we were set instances, so it is likely that key after staleSlot is the existing for (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 we find the key, then we need to swap it with the old element to maintain the hash table order. / / you can then pass the old index position obtained after exchange / or any other old index location encountered above it to expungeStaleEntry to clean up the old bar / / overwrite valueif (k = = key) {e.value = value if the same value of key is encountered / / I positions are exchanged with staleSlot old data locations to standardize array element positions and maintain hash table order / / it is necessary to maintain hash table order here. For example, recall that the judgment of previous threadLocal.set instances is linear detection to find locations that can be assigned / / if hash order is not maintained It is possible to re-maintain the hash table order tab [I] = tab [staleSlot] when the same instance is assigned multiple times, including where the old elements are cleaned later. Tab [staleSlot] = ebank / Start expunge at preceding stale entry if it exists// starts to clean up the previous old element / / if the previous old element looked forward or backward does not exist, that is, slotToExpunge = = staleSlot// at this time slotToExpunge = I, the element in position I is the old element and needs to be cleaned / / slotToExpunge to store the first old element location if (slotToExpunge = = staleSlot) slotToExpunge = I / / after cleaning up the slotToExpunge position and its subsequent non-empty consecutive positions, try to clean up the old elements in some other locations by calling cleanSomeSlots / / cleanSomeSlots does not guarantee to clean up all the old elements, its time complexity is O (log2n), it is only full cleaning of the old elements or non-clean compromise cleanSomeSlots (expungeStaleEntry (slotToExpunge), len); return } / / If we do not find stale entry on backward scan, the// first stale entry seen while scanning for key is the// first still present in the run.// if the old element previously looked forward does not exist, that is, slotToExpunge = = staleSlot, and the position I is the old element, so assign I to slotToExpunge// slotToExpunge to store the first old element location if (k = = null & & slotToExpunge = = staleSlot) slotToExpunge = I } / / If key not found, put new entry in stale slot// if no key is found by traversing the non-empty entry, then directly assign a value to the current staleSlot old element location tab [staleSlot] .value = null;tab [staleSlot] = new Entry (key, value) / / If there are any other stale entries in run, expunge them// traverses forward / backward according to staleSlot. If old elements are found, clean up if (slotToExpunge! = staleSlot) / / after cleaning the slotToExpunge position and its subsequent non-empty consecutive positions, try to clean up some old elements in other locations by calling cleanSomeSlots / / cleanSomeSlots does not guarantee to clean up all old elements, its time complexity is O (log2n) He just completely cleans up the old elements or does not clean up the compromise cleanSomeSlots (expungeStaleEntry (slotToExpunge), len) }

ExpungeStaleEntry method

The expungeStaleEntry method is executed for all the old elements found. ExpungeStaleEntry is frequently used and is the core method for cleaning up old elements. What this method does is clean up all the old elements that are continuously empty after the staleSlot position and rehash it, returning to the first null position after the staleSlot. The code and detailed comments are as follows

Private int expungeStaleEntry (int staleSlot) {Entry [] tab = table;int len = tab.length;// expunge entry at staleSlot// clears the element staleSlot [staleSlot] .value = null;tab [staleSlot] = null;size--;// Rehash until we encounter null// after the old location is cleaned, the subsequent elements need to be re-hashed into the array until it is encountered that the array position is null. That is, to maintain the hash order. Entry ThreadLocal int I for (I = nextIndex (staleSlot, len); (e = tab [I])! = null;i = nextIndex (I, len)) {ThreadLocal k = e.get (); if (k = = null) {/ / k = = null indicates that this location is also old data and needs to be cleaned up e.value = null;tab [I] = null;size--;} else {int h = k.threadLocalHashCode & (len-1) / re-hash the non-empty position after the staleSlot, and if it is different from the current position, move forward to the first empty position after the h position (including h) if (h! = I) {tab [I] = null;// Unlike Knuth 6.4 Algorithm R, we must scan until// null because multiple entries could have been stale.while (tab [h]! = null) h = nextIndex (h, len); tab [h] = e;} return I;}

CleanSomeSlots method

CleanSomeSlots is a smart method. Just like his name "some". This method is just a tentative search for some old elements. This method is called when adding a new element or replacing an old one. Its execution complexity is log2 (n), which is a compromise between "no cleanup" and "full cleanup". Return true if an old element is found. The code and detailed comments are as follows

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) } / / n > = 1 unsigned move 1 bit to the right, that is, the number of moves is based on the position of 1 of the highest binary bit of n / so the time complexity log2 (n)} while ((n > = 1)! = 0); return removed;}

Rehash/expungeStaleEntries/resize method

After the set value is successful, the rehash method is called if the program thinks that the table space is insufficient by the threshold judgment.

Rehash does two things: first, full traversal to clean up the old elements, and then determine whether the capacity is sufficient after cleaning, and if so, double the capacity and re-hash.

ExpungeStaleEntries cleans up all the old elements, while resize expands it twice as much.

/ / rehash fully traverses and cleans up the old elements, and then determines that if the capacity is greater than the threshold of 3max / 4, the method private void rehash () {/ / full traversal to clean up the old elements expungeStaleEntries () will be called when the new hash / / program considers that the table space is insufficient. / / Use lower threshold for doubling to avoid hysteresis// appropriate expansion to avoid excessive location conflicts if (size > = threshold-threshold / 4) / / 2x expansion and re-hash resize () when hash is hashed to the array;} / / full traversal to clean up the old element 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);}} / double capacity private void resize () {Entry [] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2tourism entry [] newTab = new Entry [newLen]; int count = 0Tring for (int j = 0; j < oldLen; + + j) {Entry e = oldTab [j]; 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-newLen;}} setThreshold (newLen); size = count;table = newTab;}

Get method of ThreadLocal

ThreadLocal's get logic is much simpler than set. He just hashes the threadLocal object into an array and finds the matching value by linear probing. The code and detailed comments are as follows

Public T get () {Thread t = Thread.currentThread (); ThreadLocalMap map = getMap (t); if (map! = null) {ThreadLocalMap.Entry e = map.getEntry (this); if (e! = null) {@ SuppressWarnings ("unchecked") T result = (T) e.valuescape return result;}} / / if map does not initialize a ThreadLocalMap object return setInitialValue () with key to the current threadLocal value null for null } private Entry getEntry (ThreadLocal key) {int I = key.threadLocalHashCode & (table.length-1); Entry e = table [I]; if (e! = null & & e.get () = = key) if the hash cannot be found directly, call getEntryAfterMissLinear probe to find the expected element return getEntryAfterMiss (key, I, e);} private Entry getEntryAfterMiss (ThreadLocal key, int I, Entry e) {Entry [] tab = table;int len = tab.length / / Linear probe finds matching elements. If old elements are encountered, clean up while (e! = null) {ThreadLocal k = e.get (); if (k = = key) return escape if (k = = null) expungeStaleEntry (I); elsei = nextIndex (I, len); e = tab [I];} return null;}

Remove method

The remove is about to empty the reference and call the clean up old element method. So remove does not produce old elements, and when we confirm what needs to be removed, we use the remove method to clean it first, and try not to leave it to GC. Prevent get/set from discovering too many old elements.

Public void remove () {ThreadLocalMap m = getMap (Thread.currentThread ()); if (m! = null) m.remove (this);} private void remove (ThreadLocal key) {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)]) {if (e.get () = key) {e.clear (); expungeStaleEntry (I); return;}

This is the end of the content of "what is the source knowledge of java ThreadLocal". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report