ThreadLocal

ThreadLocal is generally used to store values that are private to the current thread and not shared by other threads. In addition, multiple threads can obtain values that are unique to the current thread according to the same ThreadLocal object GET, which is isolated from other threads.

Its convenience is that instead of passing one or more parameters through the method stack, you can pass them through a shared variable, similar to a static class. Method to get a value.

Implement a ThreadLocal yourself

First, thread data isolation. How? We can take the id of the thread as the unique primary key, corresponding to the value of that ID,

Therefore, you can choose to use Map data structure, as storage, key is the thread ID, value is the corresponding value of the thread ID, the implementation of the source code is as follows:

Public class Main {static MyThreadLocal threadLocal = new MyThreadLocal(); Public static void main(String[] args) {// threadlocal.set ("user2"); New Thread(() -> {threadlocal.set ("user1"); // Print the name of the current new thread doSomething(); }).start(); // Print the name of the main method doSomething(); } private static void doSomething() {system.out.println ("thread: "+ thread.currentThread ().getName()+ ": " + threadLocal.get()); } private static class MyThreadLocal {// Use the thread name map <Long, String > data = new ConcurrentHashMap<>(); Public String get() {return data.get(thread.currentThread ().getid ()); } public void set(String userName) {data.put(thread.currentThread ().getid (), userName); }}}Copy the code

Running results:

If you look at the code in the main method, you call the doSomething method twice, but you get different results, which again illustrates the counterintuitive nature of multithreaded programming,

Similarly, we can use ThreadLocal to get the thread information of the current caller in the same method, make different processing results, give the current thread,

For example, if you have two threads, an administrator and an ordinary worker, they both call the doSomething method. The things in the method are the same, but the names are different. One says I’m an administrator, and the other says I’m an ordinary worker.

Because the two threads have different identities, the output results are naturally different.

The real implementation of ThreadLocal

Let’s just replace the MyThreadLocal class on top with the one on the bottom, which doesn’t use Map, but the JDK ThreadLocal class,

static class UserContext { static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public void set(Integer userId) { threadLocal.set(userId); } public Integer get() { return threadLocal.get(); }}Copy the code

In our own imagination, the ThreadLocal class should also have a Map that holds information for each thread,

Get () = Map (); get() = Map ();

public T get() { 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; return result; } } return setInitialValue(); }Copy the code

ThreadLocalMap map = getMap(t); , here is a place to get a map, the source code is:

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

As you can see, the ThreadLocalMap is fetched from a variable in the current thread. Why is it stored in the thread instead of in the ThreadLocal class?

Each thread has an implicit copy of its data, so that the thread’s data is garbage collected as it is destroyed.

Prevent the ThreadLocal class from referencing data from a thread that has already been destroyed, so that the garbage data cannot be collected.

* <p>Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and  the {@code ThreadLocal} * instance is accessible; after a thread goes away, all of its copies of * thread-local instances are subject to garbage collection (unless other * references to these copies exist).Copy the code

When was the map created and assigned? Take a look at the set method:

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

The set method obtains the map, if not, creates the map, and assigns the attributes of the thread:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
Copy the code

As you can see, the JDK source code consideration is very detailed, generally normal to think of through a large map to save, but the JDK also takes into account the garbage collection of data, it is worth learning the idea ~ read more Java source code will have a lot of intentions not to receive ~