Java interview must ask: ThreadLocal ultimate chapter!

Java interview must ask: ThreadLocal ultimate chapter!

Opening remarks

Zhang San's recent weather is very hot and not in a very good mood, so he decided to go out for an interview and chat with the interviewer to solve it. As a result, someone asked for an interview as soon as he submitted his resume.

I lost it. Why did someone ask me for an interview right after the delivery? Eh. . . It's really annoying, brother has been out of Jianghu for so long, there are still legends of brother in Jianghu, am I still so popular? Too annoying, handsome and innocent.
Java interview must ask: ThreadLocal ultimate chapter!
Secretly pleased Zhang San came to the office of a certain Dong on-site interview, I lost it, this interviewer? No, this Mac is full of scratches. Is this the legendary architect?
Java interview must ask: ThreadLocal ultimate chapter!
Zhang San's mentality suddenly collapsed, and he met a top interviewer in the first interview. Who can stand it.

Hello, I’m your interviewer Tony. You should be able to guess who I am by looking at my hair style. I don’t want to say anything. Shall we just start? Seeing that your resume has written multiple threads, come and talk to me about ThreadLocal. I haven't written code for a long time and I am not familiar with it. Please help me to recall.

I lost? Is this TM human? What kind of logic is this, saying that it asks for multiple threads and then comes up with such an unpopular ThreadLocal? The mentality is broken, besides, you forgot to read the book. What the hell is it to come to me to find the answer...
Java interview must ask: ThreadLocal ultimate chapter!
Although he is very reluctant, Zhang San still runs his little head at high speed, recalling ThreadLocal The details...

The interviewer told the truth that I didn’t use ThreadLocal in many places in the actual development process. When I wrote this article, I deliberately opened dozens of projects on my computer and searched ThreadLocal globally and found that in addition to the use of system source code, It is rarely used in projects, but there are still some.
Java interview must ask: ThreadLocal ultimate chapter!
The main function of ThreadLocal is to isolate data. The filled data only belongs to the current thread. Variable data is relatively isolated from other threads. In a multithreaded environment, how to prevent your own variables from being tampered with by other threads.

Can you tell me what is the use of isolation and what scenarios will it be used in?

This, I said that I rarely use it, and asked me, it's uncomfortable, oh oh oh, come to think of it, transaction isolation level.

Hello, interviewer. In fact, the first thing I thought of was the source code of Spring to achieve transaction isolation level. This was when my girlfriend was dumped by my university at the time, and I accidentally discovered it when I was crying in the library.
Java interview must ask: ThreadLocal ultimate chapter!
Spring uses the Threadlocal method to ensure that the database operations in a single thread use the same database connection. At the same time, this method can make the business layer use transactions without perceiving and managing the connection object, through the propagation level, clever management Switch between multiple transaction configurations, suspend and resume.

In the Spring framework, ThreadLocal is used to achieve 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");

  ……

Spring's affairs are mainly implemented by ThreadLocal and AOP. I will mention here. Everyone knows that each thread's own link is saved by ThreadLocal. I will elaborate on the details in the Spring chapter. Warm?

Except for the scenario where ThreadLocal is used in the source code, do you use it yourself? How would you use it in general?

Here comes the extra points. I have really encountered this. The opportunity to pretend to be B finally came.
Java interview must ask: ThreadLocal ultimate chapter!
Some interviewers, I will! ! !

Before we went online and found that the dates of some users were actually wrong. We checked the SimpleDataFormat's pot. At that time, we used the parse() method of SimpleDataFormat. There was a Calendar object inside. Calling the parse() method of SimpleDataFormat would first call Calendar.clear( ), and then call
Calendar.add(). If one thread first calls add() and then another thread calls clear(), the parsing time of the parse() method is wrong.

In fact, it is very simple to solve this problem. Just let each thread have a new SimpleDataFormat of its own, but is 1000 threads new 1000 SimpleDataFormat?

So at that time we used the thread pool and ThreadLocal to wrap SimpleDataFormat, and then called initialValue to make each thread have a copy of SimpleDataFormat, thus solving the thread safety problem and improving performance.

that……

And there is, I have, don't worry about asking the next one, let me add some points and delay the interview time.

I have a thread in the project that often encounters the object that needs to be passed across several method calls, that is, the context (Context), which is a state, often the user identity, task information, etc., there will be transition parameters The problem.

It is very troublesome to add a context parameter to each method when using a similar responsibility chain model, 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 used ThreadLocal to do it. Make a transformation so that you only need to set the parameters in ThreadLocal before calling, and get it elsewhere.

before

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

then

void work(User user) {
try{
   threadLocalUser.set(user);
   // 他们内部  User u = threadLocalUser.get(); 就好了
    getInfo();
    checkInfo();
    setSomeThing();
    log();
    } finally {
     threadLocalUser.remove();
    }
}

I have looked at cookies in many scenarios. Data isolation such as session is achieved through ThreadLocal.

By the way, my interviewer allowed me to show off the breadth of knowledge. In Android, the Looper class uses the characteristics of ThreadLocal to ensure that only one Looper object exists in each 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, how does this guy know so many scenes? I also rip out Android, isn't it, Sir, I want to test his principles below.

Hmm, you answered very well, can you tell me about his underlying implementation principle?

Good interviewer, let me talk about his use first:

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

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

It can achieve data isolation between threads, so other threads can't get the value of other threads using the get() method, but there are ways to do it, I will talk about it later.
Let's first look at the source code of his set:

public void set(T value) {
    Thread t = Thread.currentThread();// 获取当前线程
    ThreadLocalMap map = getMap(t);// 获取ThreadLocalMap对象
    if (map != null) // 校验对象是否为空
        map.set(this, value); // 不为空set
    else
        createMap(t, value); // 为空创建一个map对象
}

You can find that the source code of set is very simple, mainly ThreadLocalMap we need to pay attention to, and ThreadLocalMap is obtained from a variable called threadLocals in the current thread 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 out the truth about ThreadLocal data isolation. Each thread Thread maintains its own threadLocals variable, so when each thread creates ThreadLocal, the data is actually stored in the threadLocals variable of its own thread. No way to get it, thus achieving isolation.

What is the underlying structure of ThreadLocalMap?

The interviewer asked this question well and cursed inwardly, can't you let me rest for a while?

Zhang San smiled and replied, 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 his Entry inherits WeakReference (weak reference). I don't see 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 looks like this:
Java interview must ask: ThreadLocal ultimate chapter!

Wait a minute, can you answer my two questions?

Okay, say the interviewer.

Why do you need an array? How to solve the Hash conflict without the linked list?

The reason for using arrays is that in our development process, a thread can have multiple TreadLocals to store different types of objects, but they will all be placed in the ThreadLocalMap of your current thread, so arrays must be stored.
As for the Hash conflict, let's look at the source code first:

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 storing. During the insertion process, according to the hash value of the ThreadLocal object, locate the position i in the table, int i = key.threadLocalHashCode & (len- 1).

Then it will judge: if the current position is empty, initialize an Entry object and place it at position i;

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

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

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

If the position i is not empty and the key is not equal to entry, then find the next empty position until it is empty.
Java interview must ask: ThreadLocal ultimate chapter!
In this case, when getting, it will also locate the position in the table according to the hash value of the ThreadLocal object, and then determine whether the key in the Entry object at that position is consistent with the key of the get. If it is inconsistent, determine the next position, set If the conflict with get is serious, the efficiency is still very low.

The following is the source code of get, does it feel good to understand:

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;
// get的时候一样是根据ThreadLocal获取到table的i值,然后查找数据拿到后会对比key是否相等  if (e != null && e.get() == key)。
            while (e != null) {
                ThreadLocal<?> k = e.get();
              // 相等就直接返回,不相等就继续查找,找到相等位置。
                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, the stack memory belongs to a single thread, and each thread has a stack memory. The variables stored in it can only be seen in the thread to which it belongs. That is, the stack memory can be understood as the private memory of the thread, and the objects in the heap memory Visible to all threads, objects in heap memory can be accessed by all threads.

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

Actually not, because the ThreadLocal instance is actually held by the class it created (the top should be held by the thread), and the value of ThreadLocal is actually held by the thread instance. They are all located on the heap, just through some The trick changes the visibility to thread visibility.

What if I want to share thread ThreadLocal data?

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

private void test() {    
final ThreadLocal threadLocal = new InheritableThreadLocal();       
threadLocal.set("帅得一匹");    
Thread t = new Thread() {        
    @Override        
    public void run() {            
      super.run();            
      Log.i( "张三帅么 =" + threadLocal.get());        
    }    
  };          
  t.start(); 
} 

In the child thread, I can normally output that line of log. This is also the problem of data transfer between the parent and child threads mentioned in the interview video before.

How did it pass?

The logic of the transfer is very simple. When I mentioned threadLocals in the Thread code at the beginning, you can look down and I deliberately put another variable: In the
Java interview must ask: ThreadLocal ultimate chapter!
Thread source code, let's see what Thread.init did when it was initialized and created:

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

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, then I will give the inheritThreadLocals of the parent thread to the inheritThreadLocals of the current thread.

Isn’t it interesting?
Java interview must ask: ThreadLocal ultimate chapter!

Boy, you really know a lot, so you are a deep ThreadLocal user. Have you found any problems with ThreadLocal?

Are you talking about memory leaks?

I lost, why does this kid know what I want to ask? Yeah, right, tell me.

This problem does exist. Let me tell you why. Do you remember the code above?
Java interview must ask: ThreadLocal ultimate chapter!
ThreadLocal will treat itself as a Key when it is stored in the ThreadLocalMap. Normally, it should be that both the key and value should be strongly referenced by the outside world, but now the key is designed to be a weak reference to WeakReference.
Java interview must ask: ThreadLocal ultimate chapter!

Let me introduce you to weak references 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, no matter if the current memory space is sufficient, it will be recycled. Memory.
However, because the garbage collector is a low-priority thread, objects that only have weak references may not be found quickly.

This leads to a problem. ThreadLocal will be recycled when GC occurs when there is no external strong reference. If the thread that created the ThreadLocal continues to run, then the value in the Entry object may not be recycled, and memory leaks may occur. .

For example, the threads in the thread pool are reused. After the previous thread instance is processed, the thread is still alive for the purpose of reuse. Therefore, the value set by ThreadLocal is held, causing memory leaks.

According to the truth, if a thread is used up, ThreadLocalMap should be emptied, but now the thread is reused.

How to solve it?

Just use remove at the end of the code, as long as we remember to clear the value with remove at the end of use.

ThreadLocal<String> localName = new ThreadLocal();
try {
    localName.set("张三");
    ……
} finally {
    localName.remove();
}

The source code of remove is very simple, find the corresponding values ​​and leave them blank, so that they will be automatically recycled when the garbage collector collects them.

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

If the key is not set as a weak reference, it will cause the same memory leak as the value in the entry.
One point to add: I think the shortcomings of ThreadLocal can be made up by looking at netty's fastThreadLocal. If you are interested, you can get well.

Well, not only did you answer all my questions, you even said that I didn't know you, ThreadLocal you passed, but the interview of JUC has just begun. I hope you will be more brave in the future, and finally get a good offer.

What the hell, suddenly so sensational, isn't it embarrassing me? Is it to train me? It's difficult for the master to think about me in this way, and I kept scolding him in my heart, and went back to study hard.

to sum up

In fact, the usage of ThreadLocal is very simple. There are only a few methods in it. There are not many lines in the source code of the comment. I went through it in more than ten minutes, but when I dig into the logic behind each method, I also let I have to sigh for the greatness of Josh Bloch and Doug Lea.

In fact, the handling of detailed design is often the difference between us and the great gods. I think that many unreasonable points are only reasonable after Google and myself continue to understand them.
I really don't accept it.

ThreadLocal is a relatively unpopular class in multithreading, and its frequency of use is not as high as other methods and classes. But through my article, I wonder if you have a new understanding?
Java interview must ask: ThreadLocal ultimate chapter!
This is Ao Bing. The more you know, the more you don’t know. See you next time!

Guess you like

Origin blog.51cto.com/14689292/2545522