Please note the original source, thank you!

preface

It’s not uncommon for an interviewer to examine ThreadLocal during the interview process, so it’s important to understand it well.

Some interviewers ask straight questions like:

  • “Know about ThreadLocal?”
  • “Tell me what you understand about ThreadLocal.”

Of course, there are also interviewers who will slowly lead to this topic, such as “in a multi-threaded environment, how to prevent your variables from being tampered with by other threads”, leave the initiative to you, the rest is up to you.

So what can ThreadLocal do? Before we look at its application scenarios, let’s take a look at how it works. Only when we know how it works can we determine if it fits our business scenarios.

What is a ThreadLocal

First, it is a data structure, somewhat like a HashMap, that can hold “key: value” key-value pairs, but a ThreadLocal can hold only one, and the data of each thread is not interfering with each other.

ThreadLocal<String> localName = new ThreadLocal();
localName.set("Grab the Wolf");
String name = localName.get();
Copy the code

Localname.get () initializes a ThreadLocal object localName in thread 1 and holds a set of values. Localname.get () in thread 1 will fetch the set value, but in thread 2 it will fetch a null.

Why and how is this achieved? However, as mentioned earlier, ThreadLocal ensures that data from each thread does not interfere with each other.

Look at the source code for the set(T value) and get() methods

 public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map ! =null)
        map.set(this, value);
    else
        createMap(t, value);
}

public T get(a) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map ! =null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if(e ! =null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            returnresult; }}return setInitialValue();
}

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

As you can see, each thread has a ThreadLocalMap data structure whose value is stored in the threadLocals variable of the current thread when executing the set method, and is retrieved from the threadLocals variable of the current thread when executing the set method.

So the value of a set in thread 1 is invisible to thread 2, and if you set it again in thread 2, it doesn’t affect the value in thread 1, so that the threads don’t interfere with each other.

What exactly is a ThreadLoalMap in each thread?

ThreadLoalMap

This article analyzes the 1.7 source code.

As you might guess from its name, it is also a hashMap-like data structure, but in ThreadLocal, there is no Map interface implemented.

In ThreadLoalMap, an Entry array of size 16 is initialized. The Entry object is used to hold each key-value pair, except that the key is always a ThreadLocal object. The result is to put the ThreadLocal object itself into the ThreadLoalMap as a key.

It should be noted here that the Entry of ThreadLoalMap inherits WeakReference. The big difference from HashMap is that there is no next field in the Entry, so there is no linked list.

Hash conflict

If there is no list structure, what if there is a hash conflict?

Let’s look at the implementation of inserting a key-value into ThreadLoalMap

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(a); }Copy the code

Each ThreadLocal object has a hash value of threadLocalHashCode, and each time a ThreadLocal object is initialized, the hash value increases by a fixed size 0x61C88647.

1. If the current position is empty, initialize an Entry object and place it on position I. 2. Unfortunately, location I already has an Entry object. If the key of this Entry object happens to be the key to be set, reset the value in the Entry. 3. Unfortunately, the Entry object of position I has nothing to do with the key to be set, so you can only find the next empty position.

In this case, during the GET, the system will 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 of this position is the same as the key of the GET. If not, the system determines the next position

As you can see, if the set and GET conflict badly, it is inefficient because ThreadLoalMap is an attribute of Thread, so even if you control the number of elements set in your own code, you cannot control the behavior of other code.

Memory leaks

ThreadLocal can cause memory leaks. Why? Let’s take a look at Entry’s implementation:

static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}Copy the code

As we know from the previous analysis, when using ThreadLocal to store a value, an Entry object is inserted into the array of ThreadLocalMap. Normally, key-values should be stored in the Entry object as strong references. However, in the implementation of ThreadLocalMap, the key is stored in the WeakReference object.

This leads to the problem that ThreadLocal without strong external references will be reclaimed during GC. If the thread that created the ThreadLocal keeps running, then the value in the Entry object may not be reclaimed and will leak memory.

How to avoid memory leaks

When you call ThreadLocal get() and set(), it may clear Entry objects with null keys in ThreadLocalMap, so that no GC Roots can reach the corresponding values. It can be collected the next time it is GC, and of course if the remove method is called, the corresponding Entry object will be deleted.

If you use a ThreadLocal set method and don’t call the remove method explicitly, memory leaks can occur, so it’s important to develop good programming habits. Remember to call the remove method after using ThreadLocal.

ThreadLocal<String> localName = new ThreadLocal();
try {
    localName.set("Grab the Wolf"); // Other business logic} finally {localName.remove();
}
Copy the code

End I am occupy the small Wolf if you read the harvest, welcome to like and follow