The opening

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

Interviewer: Please introduce yourself first.

Angela: Hello, interviewer, I am grass three bitch, the best single (Daji not accept), grass motorcycle racer, radio gymnastics promoter, son of fire Angela, here is my resume, please have a look.

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

Angela: Yes.

right “, you read that right, ask is “proficient”, type 666 in the comments section.

Interviewer:

[thought] Mo is not a silly batch, come up to say that they are proficient, who proficient mouth, Mo is not a leng tole!

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

Angela: Yes.

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

Angela: Our project is a classified one, so I can’t tell you anything. You’d better change the question!

Interviewer: Tell me about an unclassified project, or you can just 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, ask you a personal question, why run out from pay treasure interview, can’t stand PUA?

Angela: PUA me, I don’t exist, the one who PUA me hasn’t been born yet! I’m tired of eating in the company canteen. I want a change.

Interviewer: So tell me what Threadlocal does?

Angela: Threadlocal is mainly used to isolate thread variables, which may not be intuitive.

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, Results access to daji data, the Daji Alipay money transferred away.

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

Interviewer: Then write pseudo-code for the scenario you just described. Here you go!

Angela: Ok

// A ThreadLocal that stores user information
private static final ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>();

public Response handleRequest(UserInfo userInfo) {
  Response response = new Response();
  try {
    // 1. Set user information to thread-local variables
    userInfoThreadLocal.set(userInfo);
    doHandle();
  } finally {
    // 3. Remove after use
    userInfoThreadLocal.remove();
  }

  return response;
}
    
// Business logic processing
private void doHandle () {
  // 2. Take it out for actual use
  UserInfo userInfo = userInfoThreadLocal.get();
  // Query user assets
  queryUserAsset(userInfo);
}
Copy the code

1.2.3 The steps are clear.

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

Angela: Oh, to get to the point so quickly, let me draw you a picture

Interviewer: I have seen the picture, so please explain the process in the picture to the code you wrote earlier.

Angela: No problem

  • UserInfoThreadLocal = new ThreadLocal(); userInfoThreadLocal = new ThreadLocal(); This reference points to the ThreadLocal object in the heap;
  • Then we call userInfoThreadLocal.set(userInfo); What’s going on here?

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

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

An Entry is put into the ThreadlocalMap of the current thread object. The key is a Threadlocal object and the value is userInfo.

Understanding two things makes it clear:

The ThreadLocalMap class is defined in Threadlocal.

  • First, Thread object is the carrier of Thread running in Java language. Each Thread has a corresponding Thread object, which stores some thread-related information.
  • Second, the Thread class has a member variable called ThreadlocalMap, which you can treat as a normal Map. The key holds the Threadlocal object, and the value is the value you want to bind to the Thread (thread-isolated variables), such as the user information object (UserInfo).

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

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.

ThreadLocalMap is designed to maintain thread-local variables for only one thing.

This is why ThreadLocalMap is a member of Thread, but is an internal class of Threadlocal (non-public, only package access, Thread and Threadlocal are included in java.lang). ThreadLocalMap lets the user know that the only thing ThreadLocalMap does is save thread-local variables.

Interviewer: Since this is a thread-local variable, why not use the Thread object as the key, which is more clear, and use the Thread as the key to get the Thread variable?

Angela: There’s a problem with this design, like: I have already stored user information in the thread variable, so I need to add a new thread variable. For example, if I add user location information, I will use the thread key in ThreadlocalMap. If I add a new location information, I will overwrite the original user information. The map.put (key,value) operation is familiar, so some articles on the web say that ThreadlocalMap uses threads as keys, which is nonsense.

Interviewer: What about adding geo-location information?

Angela: Create a new Threadlocal object because the key of a ThreadLocalMap is a Threadlocal object, such as a new geographic location, Threadlocal < Geo> Geo = new Threadlocal ();

Interviewer: What data structure is implemented in ThreadlocalMap?

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

The code is as follows:

class ThreadLocalMap {
 // Initial capacity
 private static final int INITIAL_CAPACITY = 16;
 // An array of elements
 private Entry[] table;
 // Number of elements
 private int size = 0;
}
Copy the code

Table is an array that stores thread-local variables. The element of the array is an Entry class, which consists of key and value. Key is a Threadlocal object, and value is the corresponding thread variable stored

In our previous example, the array storage structure is shown as follows:

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

Angela: [thinking] This interview is getting more and more interesting for the first time with ThreadlocalMap hashing.

If the list is too long (>8), it will turn into a red-black tree:

A HashMap a:

Reference Angela, public account: Angela’s blog a HashMap to the interviewer for half an hour

If a conflict occurs, ThreadlocalMap goes straight to the next neighboring node. If the neighboring node is empty, it goes straight to the next node. If the neighboring node is not empty, it goes straight to the next node until it finds an empty node. Or the number of elements exceeds the array length threshold.

If a ThreadlocalMap array has a length of 4, a hash conflict will occur when a location is stored at 1. If a ThreadlocalMap array has a length of 4, a hash conflict will occur when a location is stored at 2.

Source code (if it’s difficult to read, come back to it later) :

private void set(ThreadLocal<? > key,Object value) {
  Entry[] tab = table;
  int len = tab.length;
  For example, if the array length is 4, hashcode % (4-1) finds the index of the array to hold the element
  int i = key.threadLocalHashCode & (len-1);

  ThreadlocalMap does not hold many elements
  for(Entry e = tab[i]; e ! =null// Find an empty slot in the array (=null)e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();// If the key value is the same, it is an update operation
    if (k == key) {
      e.value = value;
      return;
    }
  // If key is empty, do the replacement cleaning action, which will be discussed later when we talk about WeakReference
    if (k == null) {
      replaceStaleEntry(key, value, i);
      return; }}// New Entry
  tab[i] = new Entry(key, value);
  // Number of array elements +1
  int sz = ++size;
  // If no element is cleared or the number of stored elements exceeds the array threshold, expand the array
  if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }// go to the end of the array and return to the head of the array.
private static int nextIndex(int i, int len) {
  return ((i + 1 < len) ? i + 1 : 0);
}

// The get() method gets thread variables based on the ThreadLocal key
private Entry getEntry(ThreadLocal
        key) {
  For example, if the array length is 4, hashCode % (4-1) finds the address of the array to query
  int i = key.threadLocalHashCode & (table.length - 1);
  Entry e = table[i];
  // If this position has a value, the key is equal
  if(e ! =null && e.get() == key)
    return e;
  else
    return getEntryAfterMiss(key, i, e); //miss (chain-address method)
}
Copy the code

Interviewer: I see the key in the ThreadlocalMap you drew in the previous picture is a WeakReference type. Could you tell me how many similar references there are 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 the Java virtual machine would rather throw outofMemoryErrors to abort the program when it runs out of memory than arbitrarily reclaim objects with strong references.
  • If an object has only soft references, the garbage collector will not reclaim it when there is enough memory; If you run out of memory, the objects are reclaimed.
  • The difference between weak and soft references is that objects with only weak references have a shorter lifetime. When the garbage collector thread scans the memory area, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient or not. However, because the garbage collector is a low-priority thread, objects that have only weak references are not necessarily found quickly.
  • As the name implies, a virtual reference is a virtual reference. Unlike the other references, virtual references do not determine the lifetime of the object. If an object holds only virtual references, it can be collected by the garbage collector at any time, just as if there were no references at all.

No problem! Embarrassed (─. ─ | | |.

Interviewer: Can you explain why the key in ThreadlocalMap is WeakReference?

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

Interviewer: Can you tell me more about it? You also mentioned earlier that objects referenced by WeakReference will be directly collected by GC (memory collector), so why not directly avoid memory leakage?

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 the ThreadlocalMap, which is a WeakReference reference. We said earlier that GC will reclaim a ThreadLocal object if it is only referenced by a WeakReference, without any strong references.

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

public static void main(String[] args) {
  Object angela = new Object(a);/ / weak references
  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
  // If Angela is null, only weak references will be left in the object
  angela = null; 
  System.gc();// There is not enough memory for automatic gc
  System.out.println(angela);//null
  System.out.println(weakReference.get());//null
}
Copy the code

You can see that once an object is referenced only by weak references, the object is reclaimed during GC.

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

Interviewer: So if ThreadLocal objects have strong references and cannot be reclaimed, why make them WeakReference?

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

So the designers of ThreadLocal assume that as soon as the scope of the ThreadLocal ends and the work is cleaned up, the GC will reclaim the key reference object, set the key to null, and ThreadLocal will do its best to ensure that the Entry is cleaned up to avoid memory leaks.

Let’s look at the code

/ / element class
static class Entry extends WeakReference<ThreadLocal<? >>{
  /** The value associated with this ThreadLocal. */
  Object value; // Key is inherited from the parent class, so there is only value

  Entry(ThreadLocal<? > k,Object v) {
    super(k); value = v; }}WeakReference inherits Reference, and key inherits the referent of the stereotype
public abstract class Reference<T{
  // This is the inherited key
  private T referent; 
  Reference(T referent) {
    this(referent, null); }}Copy the code

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

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

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

Let’s look at the source code:

class Threadlocal {
  public void remove() {
      // This is the ThreadLocalMap that gets the thread
      ThreadLocalMap m = getMap(Thread.currentThread());
      if(m ! =null)
        m.remove(this); // This is a ThreadLocal object}}class ThreadlocalMap {
  private void remove(ThreadLocal
        key) {
    Entry[] tab = table;
    int len = tab.length;
    // Calculate the position
    int i = key.threadLocalHashCode & (len-1);
    for(Entry e = tab[i]; e ! =null;
         e = tab[i = nextIndex(i, len)]) {
      / / clean up
      if (e.get() == key) {
        e.clear();
        expungeStaleEntry(i); // Clear the empty slot
        return; }}}}// This method is to do element cleanup
private int expungeStaleEntry(int staleSlot) {
  Entry[] tab = table;
  int len = tab.length;

  // Set staleSlot's value to null, and then the array elements to null
  tab[staleSlot].value = null;
  tab[staleSlot] = null;
  size--; // The number of elements is -1

  // 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 is null, the reference object is reclaimed by GC
    if (k == null) {
      e.value = null;
      tab[i] = null;
      size--;
    } else {
      // Since the number of elements is reduced, rehash the following elements
      int h = k.threadLocalHashCode & (len - 1);
      // Hash addresses are not equal, which means that the element has hash conflicts before (it should have been put here, but not here),
      // Now because some elements have been removed, it is possible that the location of the original conflict is empty. Try again
      if(h ! = i) { tab[i] =null;

        // Continue to use chain address to store elements
        while(tab[h] ! =null) h = nextIndex(h, len); tab[h] = e; }}}return i;
}
Copy the code

Interviewer: Tell me about your actual engineering experience with Threadlocal.

Angela: Yes!

Before, I talked with your interviewer about how I greatly improved the performance of more than 40 core RPC interfaces of the system in charge of alipay background. The following is the effect of one interface after stream cutting, among which Threadlocal was used.

Interviewer: Well, tell me.

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, also known as equivalent switch.

Here’s how it works:

  • The more than 40 interface according to the business meaning constant defines the interface name, such as interface name alipay. Quickquick. Follow. Angela.
  • Traffic is switched from low to high based on the interface traffic. Configure the center to configure the traffic cutting ratio and user whitelist for each interface in advance.
  • Cut flow is also particular, first according to the userId whitelist cut, then according to the percentage of the userId tail cut, completely no problem and then complete cut;
  • Insert the interface name into the ThreadLocal Set interface name in the entry of the top-level abstract template method.
  • Then I get the interface name from ThreadLocal where the interface flow is cut.

Interviewer: One last question, if I have a lot of variables in a ThreadlocalMap, wouldn’t I have to declare a lot of Threadlocal objects? There is no good solution.

Angela: Our best practice is to encapsulate the value of a ThreadLocalMap and make it a Map, so you only need one Threadlocal object.

Interviewer: Can you tell me more about it?

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

Interviewer: Yes.

Angela: I hate to talk about it.

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

The original link: mp.weixin.qq.com/s/JUb2GR4Cm…