This is the 21st day of my participation in the First Challenge 2022

The background,

In the previous three articles, we looked at how local thread variables can be passed through parent threads using InheritableThreadLocal and thread pools using the alibaba open source project TransmitableThreadLocal (see our previous blog for details). In the process of searching data, I accidentally found a new InternalThreadLocal defined in Dobbo. In fact, Dobbo InternalThreadLocal and Netty FastThreadLocal have similar ideas and similarities. In order to further understand the ThreadLocal population, use this blog to record.

Second, the instance

Simple test: use native thread invocation and FastThreadLocalThread invocation respectively (Netty’s FastThreadLocal and Ali’s TTL are similar, with the help of FastThreadLocalThread combination to use).

public static void main(String[] args) {
	
  new Thread(new Runnable() {
    @Override
    public void run(a) {
      username.set("zhangSang");
      log.info(username.get());
    }
  }).start();
	
  new FastThreadLocalThread(new Runnable() {
    @Override
    public void run(a) {
      username.set("zhangSang");
      log.info(username.get());
    }
  }).start();
}
Copy the code

The output of the two is the same, so you can get the value of the corresponding set from the run result alone. There is no difference in the output of the two, but if you trace it, you can see that the logic of the call is different.

Third, the principle of

If you are familiar with ThreadLocal, you should know that the internal logic of a ThreadLocal is a map structure that takes the ThreadLocal object as the key and stores the value as the value. The internal implementation of FastThreadLocal is an array, which is directly located by subscript. Therefore, FastThreadLocal is more efficient than ThreadLocal from the perspective of fetching and storing.

For threads that use native threads, Actually the last is the data storage Thread. ThreadLocals (ThreadLocal. ThreadLocalMap), that is used in this Thread to all FastThreadLocal finally The key=ThreadLocal

object,value=InternalThreadLocalMap exists in a node of the thread ThreadLocal. If netty encapsulated FastThreadLocalThread is used, the FastThreadLocalThread object attribute threadLocalMap is displayed.

Fastthreadlocalthreads inherit directly from Thread class threads and maintain an InternalThreadLocalMap for storing variables. Although this class is named Map, it is actually structured as an array. And the element subscript 0 is a Set

> structure that stores the currently valid FastThreadLocal.
>

public class FastThreadLocalThread extends Thread{
  private InternalThreadLocalMap threadLocalMap;
}
Copy the code

Some static methods are provided in InternalThreadLocalMap to obtain the required InternalThreadLocalMap in different ways according to the current thread type. If the thread type is FastThreadLocalThread, use the fast method. If not, use JDK native method to obtain FastThreadLocalThread.

public static InternalThreadLocalMap get(a) {
  Thread thread = Thread.currentThread();
  if (thread instanceof FastThreadLocalThread) {
    return fastGet((FastThreadLocalThread) thread);
  } else {
    returnslowGet(); }}Copy the code

Quickly obtain InternalThreadLocalMap directly from the FastThreadLocalThread object. If the thread variable map is empty, create a built-in InternalThreadLocalMap object and return it.

private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
  if (threadLocalMap == null) {
    thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
  }
  // ..
}
Copy the code

Native threads access method: get from UnpaddedInternalThreadLocalMap ThreadLocal < InternalThreadLocalMap > from acquiring InternalThreadLocalMap ThreadLocal, if Initialize one for null. Therefore, the FastThreadLocal value of the native thread is stored in the ThreadLocal with InternalThreadLocalMap as the value.

private static InternalThreadLocalMap slowGet(a) {
  ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = 
    UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
  InternalThreadLocalMap ret = slowThreadLocalMap.get();
  if (ret == null) {
    ret = new InternalThreadLocalMap();
    slowThreadLocalMap.set(ret);
  }
  return ret;
}
Copy the code

The thread object created by the FastThreadLocalThread is wrapped by the FastThreadLocalThread, and the map for storing thread variables is InternalThreadLocalMap.

1. The set value

Set variable values based on the obtained InternalThreadLocalMap for different thread types. There is a placeholder judgment, and if the value is not equal to the placeholder value, the value that needs to be stored is added to the subscript position corresponding to InternalThreadLocalMap.

public final void set(V value) {
  if (value != InternalThreadLocalMap.UNSET) {
    set(InternalThreadLocalMap.get(), value);
  }
}

public final void set(InternalThreadLocalMap threadLocalMap, V value) {
  if(value ! = InternalThreadLocalMap.UNSET) {if (threadLocalMap.setIndexedVariable(index, value)) {
      addToVariablesToRemove(threadLocalMap, this); }}}Copy the code

The final adding logic: obtain the corresponding element according to the subscript, then judge whether the obtained result value is empty, if not, create a set structure and add the value to the corresponding subscript position, otherwise directly apply the value taken out, and finally add the original set to the set.

private static void addToVariablesToRemove(a) {
  // ...
}
Copy the code
2. Get the value

Obtain pair strain value: The operation of obtain is relatively simple, directly get the corresponding subscript element from threadLocalMap and return directly. If it is equal to the UNSET placeholder, the process of initializing the map begins. Is a default value operation that can be initialized by calling FastThreadLocal#initialValue in the last initValue method.

public final V get(InternalThreadLocalMap threadLocalMap) {
  // ...
  if(v ! = InternalThreadLocalMap.UNSET) {return (V) v;
  }
  return initialize(threadLocalMap);
}
Copy the code
3. The remove method

Delete InternalThreadLocalMap[0] Set

> provides an extension point to the removed business logic via onRemoval, which is empty by default.
>

public final void remove(InternalThreadLocalMap threadLocalMap) {
  // ..
  Object v = threadLocalMap.removeIndexedVariable(index);
  removeFromVariablesToRemove(threadLocalMap, this);
  if(v ! = InternalThreadLocalMap.UNSET) {try{ onRemoval((V) v); }}}Copy the code

Four,

In fact, FastThreadLocal uses array to store data. Data is obtained and assigned by subscript, while ThreadLocal uses map structure, calculates hash value first, and performs linear detection to locate data. Therefore, FastThreadLocal should be relatively efficient under high concurrency, but one drawback of FastThread is that the index is accumulated all the time. That is, if a variable is removed, it will be marked as UNSET placeholder without being collected, which will cause unlimited expansion and trigger some problems.