What is the practical use of ThreadLocal in Java?

The old rule is to introduce ThreadLocal with an interview

Opening remarks

Zhang San is not in a good mood because the weather is very hot recently, so he decided to go out for an interview and chat with the interviewer. As a result, someone made an appointment for an interview as soon as he submitted his resume.

I lost it. What happened? Why did someone ask me for an interview as soon as it was delivered? Ah... It's annoying. I haven't been in the Jianghu for so long. There are still legends about me in the Jianghu. Am I still so popular? It's so annoying, handsome and innocent.

Secretly pleased, Zhang San came to the office of an East on-site interview. I lost it. Is this the interviewer? Isn't it? Is this scratched Mac the legendary architect?

Zhang San's mentality suddenly collapsed. He met a top interviewer in the first interview. Who can stand it.

Hello, I'm your interviewer, Tony. Looking at my hairstyle, you should be able to guess my identity. I don't say anything. Shall we start directly? Look, your resume has written multithreading. Come and talk to me about ThreadLocal. I haven't written code for a long time and I'm not familiar with it. Please help me remember.

I lost it? This is human talk? What's the logic? It's about multithreading, and then such a popular ThreadLocal? The state of mind is broken. Besides, you TM forgot. Don't you know what to read? Come to me to find the answer. What the hell

Although he was very reluctant, Zhang San still ran his small head at high speed and recalled all the details of ThreadLocal

To be honest, I don't use ThreadLocal very much in the actual development process. When I wrote this article, I deliberately opened dozens of projects on my computer and searched ThreadLocal globally. I found that in addition to the use of system source code, it is rarely used in projects, but there are still some.

ThreadLocal is mainly used for data isolation. The filled data only belongs to the current thread, and the variable data is relatively isolated from other threads. In a multi-threaded environment, how to prevent their own variables from being tampered with by other threads.

Author: Ao Bing
Link: https://www.zhihu.com/question/341005993/answer/1367225682
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
 

Can you tell me what it does for isolation and what scene it will be used in?

I said that I seldom use it, and asked me, I feel bad, oh, oh, I remember, transaction isolation level.

Hello, interviewer. In fact, the first thing I thought of was the source code of Spring to realize the transaction isolation level. This was accidentally discovered when I was dumped by my girlfriend in college and crying in the library.

Spring adopts the Threadlocal method to ensure that the database operation in a single thread uses the same database connection. At the same time, this method can make the business layer do not need to perceive and manage the connection object when using transactions, and skillfully manage the switching, suspension and recovery between multiple transaction configurations through the propagation level.

ThreadLocal is used in the Spring framework to realize this isolation, mainly in the TransactionSynchronizationManager class. The code is as follows:

private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);

 private static final ThreadLocal<Map<Object, Object>> resources =
   new NamedThreadLocal<>("Transactional resources");

 private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
   new NamedThreadLocal<>("Transaction synchronizations");

 private static final ThreadLocal<String> currentTransactionName =
   new NamedThreadLocal<>("Current transaction name");

  ......

Author: Ao Bing
Link: https://www.zhihu.com/question/341005993/answer/1367225682
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
 

Spring transactions are mainly implemented by ThreadLocal and AOP. I'll mention it here. You know that each thread's own links are saved by ThreadLocal. I'll talk about the details in the spring chapter. Are you warm?

In addition to the scene where ThreadLocal is used in the source code, do you use it yourself? How do you usually use it?

Here comes the bonus item. I've really met this one. The chance to install B finally came.

Some interviewers, I will!!!

Before we went online, we found that the date of some users was wrong. The investigation was down to the SimpleDataFormat pot. At that time, we used the parse() method of SimpleDataFormat. There was a Calendar object inside. The parse() method calling SimpleDataFormat first called Calendar.clear(), then called Calendar.add(), if a thread first called add(). Then another thread calls clear(), and the parse() method resolves at the wrong time.

In fact, it is very simple to solve this problem. Let each thread new its own SimpleDataFormat, but can 1000 threads new1000 SimpleDataFormat?

Therefore, at that time, we used thread pool and ThreadLocal to wrap SimpleDataFormat, and then called initialValue to make each thread have a copy of SimpleDataFormat, which solved the problem of thread safety and improved performance.

So

Also, I have. Don't ask the next one in a hurry. Let me add some points and delay the interview.

In my project, there is a thread that often encounters objects that need to be passed across several method calls, that is, Context. It is a state, often user identity and task information. There will be the problem of transitional parameter transfer.

Using a similar responsibility chain mode, adding a context parameter to each method is very troublesome, and sometimes, if the call chain has a third-party library that cannot modify the source code, the object parameters cannot be passed in, so I use ThreadLocal to make a modification. In this way, I only need to set the parameters in ThreadLocal before calling, and get them elsewhere.

before
  
void work(User user) {
    getInfo(user);
    checkInfo(user);
    setSomeThing(user);
    log(user);
}

then
  
void work(User user) {
try{
   threadLocalUser.set(user);
   // Their internal User u = threadLocalUser.get(); Just fine
    getInfo();
    checkInfo();
    setSomeThing();
    log();
    } finally {
     threadLocalUser.remove();
    }
}

I've seen that the cookie, session and other data isolation in many scenarios are implemented through ThreadLocal.

By the way, my interviewer allows me to show my knowledge again. In Android, the Looper class uses the ThreadLocal feature to ensure that there is only one Looper object per thread.

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Interviewer: I lost it. How do you know so many scenes? I also pulled out Android, didn't I? A sir, next I'll test his principle.

Well, you answered very well. Can you tell me the principle of its underlying implementation?

Good interviewer, let me talk about his use first:

ThreadLocal<String> localName = new ThreadLocal();
localName.set("Zhang San");
String name = localName.get();
localName.remove();

In fact, the use is really simple. After the thread comes in, it initializes a generic ThreadLocal object. After that, as long as the thread gets before remove, it can get the previous set value. Note that I'm talking about before remove.

It can achieve data isolation between threads, so other threads can't get the values of other threads by using the get () method, but there are ways to do it. I'll talk about it later.

Let's first look at the source code of his set:

public void set(T value) {
    Thread t = Thread.currentThread();// Get current thread
    ThreadLocalMap map = getMap(t);// Get ThreadLocalMap object
    if (map != null) // Check whether the object is empty
        map.set(this, value); // Not null set
    else
        createMap(t, value); // Create a map object that is empty
}

You can find that the source code of set is very simple, mainly ThreadLocalMap. We need to pay attention to ThreadLocalMap, which is obtained from a variable called threadLocals in the current Thread.

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

public class Thread implements Runnable {
      ......

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  
     ......

Here, we can basically find the truth of ThreadLocal data isolation. Each Thread maintains its own threadLocals variable. Therefore, when each Thread creates ThreadLocal, in fact, the data exists in the threadLocals variable of its own Thread, which can not be obtained by others, so isolation is realized.

What does the underlying structure of ThreadLocalMap look like?

The interviewer asked this question well. He scolded secretly. Can't you let me rest for a while?

Zhang San replied with a smile that since there is a Map, its data structure is actually very similar to HashMap, but looking at the source code, it can be found that it does not implement the Map interface, and its Entry inherits the WeakReference (weak reference) and does not see the next in HashMap, so there is no linked list.

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        ......
    }

The structure is roughly like this:

Wait a minute. I have two questions. Can you answer them?

OK, the interviewer said.

Why do I need arrays? How to solve Hash conflicts without linked lists?

The reason for using arrays is that during our development process, a thread can have multiple TreadLocal to store different types of objects, but they will all be placed in the ThreadLocalMap of your current thread, so we must store them in arrays.

As for Hash conflicts, let's first look at the source code:

private void set(ThreadLocal<?> key, Object value) {
           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)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

I see from the source code that ThreadLocalMap will give each ThreadLocal object a threadLocalHashCode when it is stored. During the insertion process, locate the position I in the table according to the hash value of the ThreadLocal object, int i = key. threadLocalHashCode & (len-1).

Then it will judge: if the current location is empty, initialize an Entry object and put it on location i;

if (k == null) {
    replaceStaleEntry(key, value, i);
    return;
}

If the position i is not empty, if the key of the Entry object is exactly the key to be set, refresh the value in the Entry;

if (k == key) {
    e.value = value;
    return;
}

If the location i is not empty and the key is not equal to entry, find the next empty location until it is empty.

In this way, when getting, it will also locate the location in the table according to the hash value of the ThreadLocal object, and then judge whether the key in the Entry object of this location is consistent with the key of get. If it is inconsistent, it will judge the next location. If the conflict between set and get is serious, the efficiency is still very low.

The following is the source code of get. Does it feel good to understand it

private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;
// In the same way, when getting, get the i value of table according to ThreadLocal, and then find the data. After getting the data, compare whether the keys are equal if (E! = null & & e.get() = = key).
            while (e != null) {
                ThreadLocal<?> k = e.get();
              // If it is equal, it will return directly. If it is not equal, continue to search and find the equal position.
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

Can you tell me where the objects are stored?

In Java, stack memory belongs to a single thread. Each thread will have a stack memory, and its stored variables can only be seen in its own thread, that is, stack memory can be understood as the private memory of the thread, while the objects in heap memory are visible to all threads, and the objects in heap memory can be accessed by all threads.

Does it mean that the instance of ThreadLocal and its value are stored on the stack?

In fact, it is not, because the ThreadLocal instance is actually held by the class it creates (the top should be held by the thread), and the ThreadLocal value is also held by the thread instance. They are all located on the heap, but the visibility is changed to be visible to the thread through some techniques.

What if I want to share ThreadLocal data of threads?

Using InheritableThreadLocal, multiple threads can access the value of ThreadLocal. We create an instance of InheritableThreadLocal in the main thread, and then get the value set by the instance of InheritableThreadLocal in the child thread.

private void test() {    
final ThreadLocal threadLocal = new InheritableThreadLocal();       
threadLocal.set("Very handsome");    
Thread t = new Thread() {        
    @Override        
    public void run() {            
      super.run();            
      Log.i( "Zhang sanshuai =" + threadLocal.get());        
    }    
  };          
  t.start(); 
}

In the sub thread, I can normally output that line of log, which is also the problem of data transmission between parent and child threads mentioned in my interview video.

How did you deliver it?

The logic of transmission is very simple. When I mentioned threadLocals at the beginning of Thread code, you can look down. I deliberately put another variable:

In the Thread source code, let's take a look at what Thread.init did during initialization:

public class Thread implements Runnable {
  ......
   if (inheritThreadLocals && parent.inheritableThreadLocals != null)
      this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  ......
}

Author: Ao Bing
Link: https://www.zhihu.com/question/341005993/answer/1367225682
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
 

I intercepted part of the code. If the inheritThreadLocals variable of the thread is not empty, as in our example above, and the inheritThreadLocals of the parent thread also exists, I will give the inheritThreadLocals of the parent thread to the inheritThreadLocals of the current thread.

Isn't it interesting?

Young man, you really know a lot. Then you are a deep ThreadLocal user. Have you found the problem of ThreadLocal?

You mean a memory leak?

I lost it. Why does this boy know what I want to ask? Uh huh, yes, you say it.

This problem does exist. Let me tell you why. Remember the code above?

ThreadLocal will store itself as a key in ThreadLocalMap when saving. Normally, both key and value should be strongly referenced by the outside world, but now the key is designed to be weakly referenced by WeakReference.

Let me introduce weak references to you first:

Objects with only weak references have a shorter life cycle. When the garbage collector thread scans the memory area under its jurisdiction, once an object with only weak references is found, its memory will be recycled regardless of whether the current memory space is sufficient or not. However, since the garbage collector is a low priority thread, it is not necessary to quickly find objects with only weak references.

This leads to a problem. When ThreadLocal has no external strong reference, it will be recycled during GC. If the thread creating ThreadLocal continues to run, the value in the Entry object may not be recycled and memory leakage may occur.

For example, the threads in the thread pool are reused. After the previous thread instances are processed, the threads still survive for the purpose of reuse. Therefore, the value set by ThreadLocal is held, resulting in memory leakage.

According to the truth, after a thread is used, the ThreadLocalMap should be cleared, but now the thread is reused.

So how?

Just use remove at the end of the code. We just need to remember to clear the value with remove at the end of the code.

ThreadLocal<String> localName = new ThreadLocal();
try {
    localName.set("Zhang San");
    ......
} finally {
    localName.remove();
}

The source code of remove is very simple. Find the corresponding values and empty them. In this way, they will be automatically recycled when the garbage collector recycles them.

Author: Ao Bing
Link: https://www.zhihu.com/question/341005993/answer/1367225682
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
 

Then why is the key of ThreadLocalMap designed as a weak reference?

If the key is not set as a weak reference, it will cause the same memory leakage scenario as the value in the entry.

One more thing: I think we can make up for the deficiency of ThreadLocal by looking at netty's fastThreadLocal. If you are interested, you can Kangkang.

Well, you not only answered all my questions, but you even said that ThreadLocal you passed, but the interview at JUC has just begun. I hope you will be more brave in the future and finally get a good offer.

What ghost? Isn't it difficult for me to be so sensational all of a sudden? Is it to exercise me? It's hard for the master to think of me like this. I've always scolded him in my heart. If I don't say it, I'll go back and study hard.

summary

In fact, the usage of ThreadLocal is very simple. There are only a few methods in it. There are not many lines of annotated source code. It took me more than ten minutes to go through it. However, when I dig deeply into the logic behind each method, I have to sigh about the strength of Josh Bloch and Doug Lea.

In fact, the handling of detail design is often the difference between us and the great God. I think many unreasonable points are reasonable only after Google and itself have a deep understanding. It's really not acceptable.

ThreadLocal is a popular class in multithreading, which is not used as frequently as other methods and classes. However, through my article, I wonder if you have any new knowledge?

Tags: Java Interview

Posted on Wed, 22 Sep 2021 21:07:34 -0400 by htmlstig