ThreadLocal

When multiple threads write to the same variable, thread safety is a problem, so locks and other methods are used. Instead of locking, we will introduce ThreadLocal, which literally means local thread. ThreadLocal allows each thread to be isolated from each other. The data is independent. If you’ve used a session and you know that session is a session, you can use it to store some basic information about the user, so that each user can get it on the server, and ThreadLocal can do the same thing. ThreadLocal stores that information in the current thread. ThreadLocal is a custom Map that only the current thread can access and no other thread can access.

Threadlocal. set(threadLocalUser) stores information about the user. Threadlocal.get () retrieves information stored by the current thread, and threadlocal.remove () removes information stored by the current thread

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** * ThreadLocal */
public class ThreadLocalTest {

    private static ThreadLocal<ThreadLocalUser> threadLocal = new ThreadLocal<ThreadLocalUser>();

    final static ExecutorService executorService = Executors.newCachedThreadPool();

    /** * gets the local thread value */
    static void getThreadLocalUser(a){
        System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());
    }

    /** * removes the local thread value */
    static void removeThreadLocalUser(a){
        threadLocal.remove();
        System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());
    }

    public static void main(String[] args) {
        executorService.submit(() ->{
            ThreadLocalUser threadLocalUser = new ThreadLocalUser();
            threadLocalUser.setUserId("123456");
            threadLocalUser.setRoleId("1");
            threadLocal.set(threadLocalUser);
            getThreadLocalUser();
            removeThreadLocalUser();
        });

        executorService.submit(() ->{
            ThreadLocalUser threadLocalUser = new ThreadLocalUser();
            threadLocalUser.setUserId("131420");
            threadLocalUser.setRoleId("2"); threadLocal.set(threadLocalUser); getThreadLocalUser(); removeThreadLocalUser(); }); executorService.shutdown(); }}Copy the code

As you can see,ThreadLocal is stored in threadLocals, which is a member variable of ThreadLocalMap, so ThreadLocal is really just a shell.

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

The source code parsing

set(value)

First we set the value for the current thread as follows.

threadLocal.set("steak");
Copy the code

Next, let’s go into the set() method and see how it works

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

Since ThreadLocal’s storage structure is itself a Map, the reference to the current ThreadLocal is used as the key. We set the value to value, so threadlocals naturally uses the current thread instance as the key to obtain the member variable.

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

GetMap (t) is actually a function of the current thread. ThreadLocals is a member variable of ThreadLocalMap. Will the current ThreadLocal reference (this) as a key to store threadLocals, threadLocals value of values can be passed to us.

map.set(this, value);
Copy the code

If threadLocals is null, then the set() or get() methods of ThreadLocal are called for the first time, and threadLocals needs to be created.

createMap(t, value);
Copy the code

CreateMap (t,value) creates a threadLocals variable and assigns it to the current thread member variable threadLocals.

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

get()

Gets the current thread variable

threadLocal.get();
Copy the code

Get threadlocals from the current thread instance. If it is not null, the value of the variable is taken out. If it is null, it needs to be initialized.

 public T get(a) {
        // Get the current thread
        Thread t = Thread.currentThread();
        // Get threadlocals based on the current thread
        ThreadLocalMap map = getMap(t);
        if(map ! =null) {
            // Get local variables
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}/ / initialization
        return setInitialValue();
    }
Copy the code

Go to the setInitialValue() method,

private T setInitialValue(a) {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
Copy the code

SetInitialValue () has an initialValue() method that returns null.

protected T initialValue(a) {
    return null;
}
Copy the code

Set (this, value). If threadlocals is null, createMap(t,value) is used to create theadlocals. As we can see, null is always returned, and setInitialValue() is basically an initialization operation.

remove()

The remove() method first gets threadlocals from the current thread instance and removes it if it is not null.

public void remove(a) {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if(m ! =null)
       m.remove(this);
}
Copy the code

InheritableThreadLocal

[InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal]

public class ExtendThreadLocalTest {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("steak");
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("Get from child thread:"+threadLocal.get());
            }
        }).start();

        System.out.println("Get from parent thread:"+threadLocal.get()); }}Copy the code

The output

Obtained from parent thread: null from steak child thread

[InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal

public class InheritableThreadLocalTest {
    private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("steak");

        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("Get from child thread:"+threadLocal.get());
            }
        }).start();

        System.out.println("Get from parent thread:"+threadLocal.get()); }}Copy the code

The output

Obtained from parent thread: steak Obtained from child thread: steak

InheritableThreadLocal inherits from ThreadLocal, uses InheritableThreadLocal to save local variables, and saves local variables in inheritableThreadLocals

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    
    protected T childValue(T parentValue) {
        return parentValue;
    }
    
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }}Copy the code

InheritableThreadLocals is also a variable of ThreadLocalMap

 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Copy the code

So when we use InheritableThreadLocal to save variables, we call the createMap(Thread t, t firstValue) of InheritableThreadLocal. The value created by inheritableThreadLocals is assigned to the inheritableThreadLocals getMap(Thread t) of the inheritableThreadLocals variable

How can the child thread access the parent thread principle analysis

If the inheritThreadLocals parameter is true, the initial value of the inheritable thread-local variable is inherited from the constructor Thread. If the inheritableThreadLocals of the parent thread is also not empty, So is called a ThreadLocal. CreateInheritedMap (parent. InheritableThreadLocals) create inheritableThreadLocals, It then assigns to the child’s inheritableThreadLocals, so that the child has the same inheritableThreadLocals as the parent.

init(g, target, name, stackSize, null.true);
Copy the code
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        Thread parent = currentThread();
        if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    }
Copy the code
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}
Copy the code

FastThreadLocal

FastThreadLocal is a Netty class that works just like ThreadLocal, but is much more efficient than ThreadLocal

public class FastThreadLocalTest {

    private static FastThreadLocal<String> threadLocal = new FastThreadLocal<String>();

    public static void main(String[] args) {
        new FastThreadLocalThread(() -> {
            threadLocal.set("liu");
            System.out.println(threadLocal.get());
        }).start();

        new FastThreadLocalThread(() -> {
            threadLocal.set("pai"); System.out.println(threadLocal.get()); }).start(); }}Copy the code

The output

pai

liu

About FastThreadLocal related principles, I will talk about later, if you are interested to learn.