Three ThreadLocal, playing with thread variable saving and transfer

ThreadLocal

ThreadLocal is a self-contained class in the jdk. It is used to save the thread specific data. You can directly see its role from the following code

 private static ThreadLocal<String> sThreadLocal=new ThreadLocal<>();

    @Test
    void testThreadlocal() {
        //Main thread
        sThreadLocal.set("This is in the main thread");
        System.out.println("Thread Name:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
        //Thread a
        new Thread(new Runnable() {
            public void run() {
                sThreadLocal.set("This is in the thread a in");
                System.out.println("Thread Name:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
            }
        },"thread  a").start();
        //Thread b
        new Thread(new Runnable() {
            public void run() {
                sThreadLocal.set("This is in the thread b in");
                System.out.println("Thread Name:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
            }
        },"thread  b").start();
        
    }

Operation results:
​​​​
Obviously, there is only one sThreadLocal variable, which is of string type, but the sThreadLocal variable values printed by the main thread and ab sub threads are different. This is the meaning of naming ThreadLocal and creating thread specific variable data.
Why can this effect be achieved? In fact, the principle is also very simple. Let's click into ThreadLocal's set method to see.

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

In this method, we can see a variable called ThreadLocalMap, which is similar to Hashmap. Our value is set here. Click again to find that the getMap method is taken from the Thread class itself. Therefore, it is easy to understand why the ThreadLocal variable is bound to the Thread, because the ThreadLocalMap that saves ThreadLocal is a variable of the Thread.

    Thread.class:
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal is generally used in conjunction with tomcat's model of a request and a business thread to save contents that may be used globally, such as user information, and avoid variable transparent transmission.

InheritableThreadLocal

As you can see from the name, this class is used for variable passing of parent-child threads. We only need to make slight modifications to the test code above

  private static InheritableThreadLocal<String> sThreadLocal=new InheritableThreadLocal<>();

    @Test
    void testThreadlocal() {
        //Main thread
        sThreadLocal.set("This is in the main thread");
        System.out.println("Thread Name:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
        //Thread a
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Thread Name:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
            }
        },"thread  a").start();
        
    }

Operation results:

We can see that there is no set sThreadLocal behavior after we create a new thread, but we can still get the sThreadLocal variable of the main thread (parent thread), which is the difference between InheritableThreadLocal and ThreadLocal. The variable can be passed from the parent thread to the child thread.

TransmittableThreadLocal

InheritableThreadLocal is rarely used in actual development. The reason is very simple. No one will directly create a new thread in formal development, which will make it difficult to manage and recycle threads. Most projects use thread pool when involving multiple threads, but this also brings a problem. The threads in the thread pool have no dependency with the main business thread, Can ThreadLocal variables only be passed through explicitly? At this time, we can use Alibaba's open source components transmittable-thread-local solve.

//Just use the wrapped thread pool directly
static ExecutorService pool =  TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(3));

If spring asynchronous annotation @ Aynsc is used, the same is true. Just customize the asynchronous thread pool as ttlExecutor
The principle of transmittable ThreadLocal is also very simple. The key is that the user-defined TtlRunnable completes the action of transferring ThreadLocal

 @Override
    public void run() {
        final Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }
        //This step is the save variable action
        final Object backup = replay(captured);
        try {
            runnable.run();
        } finally {
            restore(backup);
        }
    }

//Actual implementation of deposit transfer method
private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
            final HashMap<ThreadLocal<Object>, Object> backup = new HashMap<ThreadLocal<Object>, Object>();

            for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
                final ThreadLocal<Object> threadLocal = entry.getKey();
                backup.put(threadLocal, threadLocal.get());

                final Object value = entry.getValue();
                if (value == threadLocalClearMark) threadLocal.remove();
                else threadLocal.set(value);
            }

            return backup;
        }

Keywords: Java Software development

Added by mark_john on Wed, 22 Sep 2021 18:35:08 +0300