java ThreadLocal thread setting private variable bottom source code analysis

Keywords: Java Spring Database Session

I've heard that ThreadLocal is used to achieve high concurrency. Previously, it was implemented with locks. After reading a lot of data, I found that there is a big difference (it seems that ThreadLocal is not a high concurrency solution strictly). Now let's summarize.

The problem in high concurrency is thread safety, which can be said to be how to deal with the access of shared resources by multiple threads. If it is not handled properly, the result will be completely different from the expectation.

In general, when multiple threads access a variable, they all share their values. Sometimes, although they also access shared variables, each thread needs its own private variables. At this time, ThreadLocal will be useful. Here is a simple example of ThreadLocal:

 

public class ThreadLocalExample {
    public static void main(String[] args){
        //Create a ThreadLocal object
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        
        //Set the private variable value of the main process
        threadLocal.set(100);
        
        //Create a new thread
        new Thread(new Runnable(){
            public void run(){
                //Use shared variables to set thread private variables
                threadLocal.set(50);
                System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
            }
        }).start();
        
        System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
    }
}

 

Output results:

main:100
Thread-0:50

It's amazing how many resources can be shared without their mutual influence, so it's good to use this. For specific applications, I remember a lot of spring applications, including every connection to the database and session storage.

After thinking about it, if I want to implement it, I will use a map to store it, because this is actually a key value pair, but the key is the only identification of the thread, and the value is the corresponding private variable.

It's almost the same as the source code. However, an internal ThreadLocalMap class and an Entry class are used. The Entry class inherits the weakrefresh class (to be honest, it's the first time to encounter a weak application, but it was only learned in the jvm book). The specific methods are as follows:

  

Let's see his set method first

public void set(T value) {
    
        Thread t = Thread.currentThread();

        //Get all thread shared ThreadLocalMap object
        ThreadLocalMap map = getMap(t);

        //Insert the key value pair directly when the object already exists
        //Create and insert if it doesn't exist
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}

For the getMap method, the ThreadLocalMap object shared by all threads is as follows:

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

Then enter the Thread class to find the container. Find the following:

ThreadLocal.ThreadLocalMap threadLocals = null;

Then create:

void createMap(Thread t, T firstValue) {
    //Create a ThreadLocalMap object to assign to threadlocales t.threadLocals
= new ThreadLocalMap(this, firstValue); }

At this point, the basic principle of ThreadLocal is very clear: each thread operates on the shared ThreadLocal instance, which is actually used as the key to operate on the internally held ThreadLocalMap object.

There is also the get() method, which is to use the set key to obtain. The remove() method is also similar to the Hashmap in fact, but it is almost the zipper method used to resolve conflicts (by the way, the next time I write a Hashmap, there is also the concurrent Hashmap, which is quite researched). There is a problem here. Because this ThreadLocalMap is static, in the method area (metadata area after jdk8), if it is not recycled, it will cause memory leak and memory overflow. So remember to remove() after use.

Basically, it's OK, but I'm curious about how the ThreadLocalMap can be implemented. I'm also curious about it, so I've also secretly read it, hehe hehe.

 

Let's analyze the inner class ThreadLocalMap.

ThreadLocalMap belongs to a custom map, which is a static internal class with hash function. In fact, it has nothing to do with the map class provided under java.util package. There is a static Entry class inside. Let's analyze the Entry.

/**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

Stole an official explanation:

It mainly means that the entry inherits from WeakReference and uses the field referenced by the main method as the key in the entry. When entry.get() == null, it means that the key will no longer be referenced.

Notice that you see a super(k), indicating the construction of calling the parent class. Go and have a look.

Reference(T referent) {
    this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

There's nothing else on the above. After reading for a long time, I didn't understand it a bit. Then I learned four kinds of quotations and came back to understand it. Because of too much space, I gave two other blogs at the end. I can read them and come back to learn more hahaha.

It is found that there are many internal classes, but it is actually an implementation of map. As mentioned above, the set method is related to the set() method of ThreadLocalMap. The code is as follows:

private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            //hold Entry Object array
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            //The length is here, too
            int len = tab.length;
            //Get by key Of hashcode Value, go in and find the magic scene. Here we use the method to accumulate the value 0. x61c88647 To act as hashcode,
            // It's mentioned here that if you want to share this property, you will have problems accessing multiple instances.
            // So I used AtomicInteger Atomic operation to write value
            //And with the total length-1 Doing and calculating are moduli, because expansion is 2. n Power, so it's good to take the mold directly. It's fast.
            int i = key.threadLocalHashCode & (len-1);

            //Locate to the corresponding array position for handling such as conflict judgment
            for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {   //Here is conflict traversal

                //Take the corresponding in here tabel The current reference of the corresponding position below
                ThreadLocal<?> k = e.get();

                //Judge whether it is the corresponding key. If yes, it will be overwritten.
                if (k == key) {
                    e.value = value;
                    return;
                }
                //If not, it will be generated. Entry Instead of
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //It's just inserted here.
            tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
            //Length plus 1
            int sz = ++size;
            //Judge whether to expand
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

 

In fact, it's quite complicated. Specifically, it's normal to use the open addressing method to handle the conflicts. Here, we use the accumulation of a fixed value to solve the conflicts, because multiple instances are shared, and special processing is very powerful.

//threadLocalHashCode The code is also posted here. If you are interested, you can go to see it directly.

        private static AtomicInteger nextHashCode = new AtomicInteger();
        private static final int HASH_INCREMENT = 0x61c88647;
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }
        private final int threadLocalHashCode = nextHashCode();

Sum up

After reading the source code, I feel refreshed and learned a lot. In the past, java reference only knew four references and corresponding simple concepts. In order to understand this Entry, I learned the source code of weakReference, read other people's blogs about four references and wrote them well. I learned them secretly and knew how to use them. The key points will be used! Of course, it can also be used for ThreadLocal, and it seems that you can write a simple version by hand. You can try it by hand.

 

It's really great to write about four kinds of reference blogs.

https://blog.csdn.net/swebin/article/details/78571933

https://blog.csdn.net/hacker_zhidian/article/details/83043270

Posted by storyboo on Mon, 28 Oct 2019 05:57:26 -0700