The opening

A battle between a job seeker and an interviewer is taking place in a business building in Hangzhou.

Interviewer: Please introduce yourself first.

Angela: Hello, interviewer, I am The grass three bitch, the strongest single (Daji not satisfied), lawn motorcycle rider, the 21st set of radio gymnastics promoter, the flame of the person Angela, this is my resume, please have a look.

Interviewer: It says on your resume that you are familiar with multithreaded programming. To what extent?

Angela: Proficient.

right You read that right, asking means “proficient.” Type 666 in the comments section.

Interviewer:

[thinks] is not a silly batch, come up to say oneself master, who talk mastery, is not a stupid green!

Interviewer: Let’s get started. Have you used Threadlocal?

Angela: Yes.

Interviewer: So tell me about the use of ThreadLocal in your project.

Angela: Our project is classified, no comment, you’d better change the question!

Interviewer: Tell me about an unclassified project, or tell me how Threadlocal works.

To the chase

Angela: Show time…

Angela: take a chestnut, we pay treasure every second at the same time there will be a lot of users request, that each request with user information, we know that is usually a thread to handle a user request, we can put the user information into the Threadlocal, let each thread handle their own user information, mutual interference between threads.

Interviewer: Wait a minute, let me ask you a personal question. Why did you come to interview from Alipay? Can’t stand PUA?

Angela: PUA me. There is no one. Anyone who can PUA me hasn’t been born yet! I’m tired of eating in the cafeteria. I want a change.

img

Interviewer: So tell me what a Threadlocal does?

Angela: It may not be intuitive to say that Threadlocal is primarily used for isolation of thread variables.

Or say the aforementioned example, we program at the time of processing user requests, usually the back-end server is A thread pool, to A request to A thread to handle, that in order to prevent the multi-thread concurrent processing request when string of data, such as AB thread processing Angela and da ji’s request, A thread was processing Angela’s request, The results of access to The data of Daji, Daji alipay money transfer.

So you can bind Angela’s data to thread A, and then unbind it when it’s done.

Interviewer: That you just said the scene with pseudo code implementation, to pen to you!

Angela: OK

Private static final ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>(); private static final ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>(); public Response handleRequest(UserInfo userInfo) { Response response = new Response(); Userinfothreadlocal. set(userInfo); // 1. doHandle(); } the finally {/ / 3. Use the remove the userInfoThreadLocal. Remove (); } return response; } private void doHandle () {// 2. UserInfo UserInfo = userInfoThreadLocal.get(); QueryUserAsset (userInfo); }Copy the code

1.2.3 The steps are clear.

Interviewer: Can you tell me how Threadlocal implements isolation of thread variables?

Angela: Oh, to get to the point so quickly, let me draw you a picture. It’s like this

Interviewer: I have seen the diagram, then you talk about the corresponding process in the diagram to the code you wrote earlier.

Angela: No problem

  • First we initialize a ThreadLocal object with ThreadLocal

    userInfoThreadLocal = new ThreadLocal(), which is the ThreadLocal reference shown in the figure above, This reference points to a ThreadLocal object in the heap;

  • Then we call userInfoThreadLocal.set(userInfo); So what’s going on here?

    Let’s take the source code out and look at it.

    We know that the Thread class has a member variable called ThreadLocalMap, where the Map key is the Threadlocal object, and the value is the thread-local variable you want to store.

    Public void set(T value) { Thread t = thread.currentThread (); ThreadlocalMap = getMap(t); ThreadlocalMap = getMap(t); if (map ! = null) // This refers to the Threadlocal object map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) {// get the Thread's ThreadLocalMap return t.treadlocals; } void createMap(Thread t, t firstValue) {// initialize t.hreadlocals = new ThreadLocalMap(this, firstValue); }Copy the code

    Public class Thread implements Runnable {// Each Thread has its own ThreadLocalMap member variable ThreadLocal.ThreadLocalMap threadLocals = null; }

    In this case, an Entry is put in the ThreadlocalMap of the current thread object. The key is the **Threadlocal object ** and the value is userInfo. Two things are clear to understand: the ThreadLocalMap class is defined in a Threadlocal.Copy the code
  • First, a Thread object is a vehicle for a Thread to run in the Java language. Each Thread has its own Thread object, which holds information about the Thread.

  • Second, the Thread class has a member variable, ThreadlocalMap, which you treat as a normal Map. The key holds the Threadlocal object, and the value is the value you want to bind to the Thread (the Thread isolated variable), such as UserInfo.

Interviewer: You said that class Thread has a member variable with the attribute ThreadlocalMap, but the definition of ThreadlocalMap is in Threadlocal. Why?

Angela: Let’s look at the instructions for ThreadlocalMap

class ThreadLocalMap
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread.  To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.

Copy the code

ThreadLocalMap is designed to maintain thread-local variables, and that’s all it does.

This is why ThreadLocalMap is a member variable of Thread, but is an inner class of Threadlocal (non-public, only package access, both Thread and Threadlocal are in the java.lang package). Let the consumer know that ThreadLocalMap only does the job of saving thread-local variables.

Interviewer: Since it is a Thread local variable, why not use the Thread object as the key, so it is clearer, directly use the Thread as the key to get the Thread variable?

Angela: There are problems with this design, such as: I’ve kept the user information in a thread variables, this time need new add a thread variables, for example new user location, we use the key ThreadlocalMap is thread, to save a geographical location information, the key is the same thread (key), not the original user information covered. If you’re familiar with map.put (key,value), some articles on the Web say that ThreadlocalMap’s use of threads as keys is nonsense.

Interviewer: What about adding location information?

Angela: Just create a new Threadlocal object, because the key of a ThreadLocalMap is a Threadlocal object, I will use Threadlocal < Geo> Geo = new Threadlocal () to store the location information, so that the thread’s ThreadlocalMap has two elements: user information and location information.

Interviewer: What data structure is ThreadlocalMap implemented?

Angela: Like HashMap, it’s an array implementation.

The code is as follows:

Class ThreadLocalMap {private static final int INITIAL_CAPACITY = 16; Private Entry[] table; Private int size = 0; }Copy the code

A table is an array that stores thread-local variables. The element of the array is an Entry class. An Entry consists of a key and a value, where key is a Threadlocal object and value is the corresponding thread variable

In our previous example, the array storage structure looks like this:

Interviewer: What if ThreadlocalMap hash conflicts? How is it different from a HashMap?

Angela: [thinking] The first time someone asks ThreadlocalMap about hash conflicts, this interview is getting more and more interesting.

If the list is too long (>8), it turns into a red-black tree. If the list is too long (>8), it turns into a red-black tree.

A HashMap a:

reference

Angela, public account: Angela’s blog had a HashMap chat with the interviewer for half an hour

ThreadlocalMap does not have a linked list or a red-black tree. Instead, ThreadlocalMap uses an open addressing method. If there is a conflict, ThreadlocalMap goes back to the next node in the contiguity. Or the number of elements exceeds the array length threshold.

As shown in the previous example, the ThreadlocalMap array has a length of 4, and now the hash collision occurs when the array is stored at location 1 (location 1 already has data).

Source code (if it’s hard to read, go back to it later) :

private void set(ThreadLocal<? > key, Object value) { Entry[] tab = table; int len = tab.length; Int I = key.threadLocalHashCode & (len-1); // If the length of the array is 4, hashcode % (4-1) finds the subscript of the array to hold the elements; For (Entry e = TAB [I]; e ! = null; E = TAB [I = nextIndex(I, len)]) {ThreadLocal<? > k = e.get(); If (k == key) {e.value = value; if (k == key) {e.value = value; return; } if (k == null) {replaceStaleEntry(key, value, I);} if (k == null) {replaceStaleEntry(key, value, I); return; TAB [I] = new Entry(key, value); Int sz = ++size; // If the number of stored elements exceeds the array threshold, expand the array if (! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } private static int nextIndex(int I, int len) {return ((I +1 < len)? i + 1 : 0); } private Entry getEntry(ThreadLocal<? > key) {// Compute the hash value & take the remainder of the % array length. For example: Int I = key.threadLocalHashCode & (table.length-1); int I = key.threadLocalHashCode & (table.length-1); Entry e = table[i]; // Return if (e! = null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); // Miss = miss;Copy the code

Interviewer: I see that the key in ThreadlocalMap is a type of WeakReference. Could you talk about some similar references in Java? What are the differences?

Angela: Sure

  • Strong references are the most commonly used references. If an object has a strong reference, the garbage collector will never reclaim it, and when memory runs out, the Java virtual Machine would rather throw an OutOfMemoryError, causing the program to terminate abnormally, than arbitrarily reclaim the object with a strong reference to solve the memory problem.

  • If an object has only soft references, then the garbage collector will not reclaim it if there is enough memory. If the memory space runs out, the memory of these objects is reclaimed.

  • The difference between a weak reference and a soft reference is that objects with only weak references have a shorter life cycle. When the garbage collector thread scans the memory region, once an object with only weak references is found, its memory is reclaimed regardless of whether the current memory space is sufficient. However, because the garbage collector is a low-priority thread, objects with only weak references are not necessarily quickly discovered.

  • A dummy reference is, as the name suggests, a dummy. Unlike the other references, virtual references do not determine the life cycle of the object. If an object holds only virtual references, it is just as likely to be collected by the garbage collector at any time as if there were no references at all.

No problem eight-part essay ah! Embarrassed (─. ─ | | |.

Interviewer: Can you explain why the key in ThreadlocalMap is designed as a WeakReference type?

Angela: Yes, in order to do our best to avoid memory leaks.

Interviewer: Can you elaborate on that? Why do we try our best? As you mentioned before, objects referenced by WeakReference will be directly collected by GC (memory collector), why not directly avoid memory leak?

Angela: Let’s look at the picture below

private static final ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>();
userInfoThreadLocal.set(userInfo);

Copy the code

The reference relationship here is that userInfoThreadLocal refers to a ThreadLocal object, which is a strong reference. The ThreadLocal object is also referenced by the key of ThreadlocalMap. This is a WeakReference reference. We said earlier that the premise for GC to reclaim ThreadLocal object is that it is only referenced by WeakReference, without any strong reference.

To make it easier for you to understand weak references, I wrote a little Demo program

public static void main(String[] args) { Object angela = new Object(); // WeakReference<Object> WeakReference = new WeakReference<>(Angela); // Angela and weak references refer to the same object system.out.println (Angela); //java.lang.Object@4550017c System.out.println(weakReference.get()); //java.lang.Object@4550017c // Set the strong reference for Angela to null, and the object will only have weak references. Angela = null; System.gc(); System.out.println(Angela); //null System.out.println(weakReference.get()); //null }Copy the code

As you can see, once an object is only referenced by a weak reference, GC will reclaim the object.

Therefore, as long as the ThreadLocal object is also referenced by userInfoThreadLocal (strong reference), GC will not reclaim the object referenced by WeakReference.

Interviewer: Since ThreadLocal object has a strong reference, it cannot be recycled, so why design it as a WeakReference type?

Angela: The designers of ThreadLocal took into account that threads tend to have long lifetimes. For example, Thread pools are often used. Threads are always alive, and according to the ROOT search algorithm of the JVM, there is always a reference link such as Thread -> ThreadLocalMap -> Entry (element), as shown in the figure below. If the key is not designed as a WeakReference type and is a strong reference, it will not be collected by GC, the key will not be null, and the Entry element will not be cleaned up if it is not null (ThreadLocalMap determines whether to clean up the Entry according to whether the key is null).

So the designers of ThreadLocal think that as long as the scope where the ThreadLocal is is finished and cleaned up, GC will recycle the key reference object, set the key to null, and ThreadLocal will try its best to clean up the Entry to avoid memory leaks as much as possible.

So let’s look at the code

Static Class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; // The key is inherited from the parent class, so only value Entry(ThreadLocal<? > k, Object v) { super(k); value = v; } //WeakReference inherits Reference; Public abstract class Reference<T> {private T referent; Reference(T referent) { this(referent, null); }}Copy the code

Entry inherits WeakReference class. Key in Entry is of WeakReference type. In Java, when an object is only referenced by WeakReference and no other object is referenced, Objects referenced by WeakReference will be reclaimed directly when GC occurs.

Interviewer: What if Threadlocal objects always have strong references? There’s a risk of memory leaks.

Angela: The best practice is to call the remove function manually after use.

Let’s look at the source code:

ThreadLocalMap m = getMap(thread.currentThread ()); ThreadLocalMap m = getMap(thread.currentThread ()); if (m ! = null) m.remove(this); }} class ThreadlocalMap {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)]) {/ / clean up the if (um participant et () = = key) {e.c. with our fabrication: Lear (); expungeStaleEntry(i); // Clean the empty slot return; Private int expungeStaleEntry(int staleSlot) {Entry[] TAB = table; int len = tab.length; // Set staleSlot's value to null, then set array elements to null TAB [staleSlot]. Value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) ! = null; i = nextIndex(i, len)) { ThreadLocal<? > k = e.get(); If (k == null) {e.value = null; tab[i] = null; size--; Int h = k.treadLocalHashCode & (len - 1); // If the hash address is not equal, it means that the hash address of the element is not equal. // If the hash address is not equal, it means that the hash address of the element is not equal. = i) { tab[i] = null; // While (TAB [h]! = null) h = nextIndex(h, len); tab[h] = e; } } } return i; }Copy the code

Interviewer: Tell me about your engineering experience with Threadlocal.

Angela: Yes!

I have talked with one of your interviewers before about how I greatly improved the performance of more than 40 core RPC interfaces of the system responsible for the backstage of Alipay. The following is the effect of one of the interfaces after the tangleflowThreadlocal.

Interviewer: Well, tell me about it.

Angela: I just said that there are more than 40 interfaces to be modified and optimized. The risk is very high. I need to ensure that the business will not be affected after the interface switch.

The process goes like this:

  • The more than 40 interface according to the business meaning constant defines the interface name, such as interface name alipay. Quickquick. Follow. Angela.

  • The tangential ratio and user whitelist of each interface are configured in the configuration center in advance.

  • Tangential flow is also important. First, the userId whitelist is used for tangential flow. Then, the userId trailing percentage is used for tangential flow.

  • At the top level of the abstract template method, insert the name of the interface through the ThreadLocal Set.

  • Then I get the interface name from ThreadLocal where the tangential flow is, which is used to determine the tangential flow.

Interviewer: One last question, if I have a lot of variables to put into a ThreadlocalMap, wouldn’t I have to declare a lot of Threadlocal objects? Is there a good solution?

Angela: Our best practice is to reencapsulate the value of a ThreadLocalMap by making it a Map, so that only one Threadlocal object is needed.

Interviewer: Can you elaborate on that?

Angela: I can’t. I’m so tired.

Interviewer: Tell me about it.

Angela: I hate to talk about it.

Interviewer: that today first arrive here, you go out this door to turn right, go back wait for notice!