ThreadLocal application and principle, weak reference introduction, data sharing in the same thread

Keywords: Java

Record the learning content. If there are mistakes, thank you for correction, communicate with each other and make progress together

ThreadLocal

Summary:

In the heap memory, there is a thread shared hash table ThreadLocalMap, which can be used to transfer parameters in the thread, such as user information, application information and other basic information, so that there is no need to transfer repeatedly in the process of method call. ThreaLocal is more like a tool object operating the table

Diagram

 

//Execute application code
ThreadLocal<String> userId=new ThreadLocal<String>()
userId.set("1234")
//ThreadLocal source code
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
// ThreadLocalMap source code
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

When we create a ThreadLocal object userId and assign 1234, we find through the source code that the value content "1234" is not saved in the ThreadLocal object, but obtains the current thread object through ThreadLocal, and obtains the ThreadLocalMap object corresponding to the current thread through this object. The value content "1234" is saved in the value of the Entry object in ThreadLocalMap, Key is an instance of ThreadLocal object. Note that key is a weak reference, which will be described later

ThreadLocal application

Scenario 1: sharing basic information

1. Create a tool class UserRuntimeEnv to define the basic information object

public class UserRuntimeEnv {
    //USERID and PHONE here are the key s of ThreadLocal object
    private static final ThreadLocal<String> USERID=new ThreadLocal<>();
    private static final ThreadLocal<String> PHONE =new ThreadLocal<>();

    public UserRuntimeEnv() {
    }
    public static String getPhone(){
        return (String)PHONE.get();
    }
    public static void setPhone(String phone){
        PHONE.set(phone);
    }

    public static void setUserId(String userId) {
        USERID.set(userId);
    }

    public static String getUserId(){
        return USERID.get();
    }

    public static void clear(){
        PHONE.remove();
        USERID.remove();
    }


}

  2. Create Aop class to intercept requests and store basic information (the content of user-defined annotations can be ignored)

 public Object aroundApi(ProceedingJoinPoint point)throws Throwable{
        String args=argsToString(point.getArgs());
        log.info("Unified log printing ↓ ↓ ↓ ↓ ↓ ↓ {}.{}() start ↓ ↓ ↓ ↓ ↓ ↓ ↓ ,Parameters:\n{}",
                point.getSignature().getDeclaringTypeName(),//Class name
                point.getSignature().getName(),//Method name
                args);//Request parameters
        StopWatch stopWatch=new StopWatch();//spring universal time recording tool, ms level
        stopWatch.start();
        RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();
        HttpServletRequest request=(HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        String userId=request.getHeader("userId");
        UserRuntimeEnv.setUserId(userId);//The user information is stored in the thread local table to facilitate the method to directly call and obtain parameters
        MethodSignature methodSignature=(MethodSignature) point.getSignature();//Get method object
        String declaringTypeName=methodSignature.getDeclaringTypeName();
        String methodName=methodSignature.getName();

        Object response=null;
        //Get the LogPrint annotation information of the method, which may be null
        LogPrint logPrint=methodSignature.getMethod().getAnnotation(LogPrint.class);
        try {
            response=point.proceed();
        }finally {
            stopWatch.stop();
            if(null==logPrint||logPrint.isPrint()){
                log.info("Unified log printing(end): {}.{}() ↑ ↑ ↑ ↑ ↑,response time:{}millisecond,Response content:\n{}",
                        declaringTypeName, methodName, stopWatch.getTotalTimeMillis(), argsToString(response));
            }
        }
        //In this case, the thread has ended and can be cleared or not
        UserRuntimeEnv.clear();
        return response;
    }

  3. It can be used in the method execution of the whole thread

UserRuntimeEnv.getUserId get the corresponding user information

Weak reference

abstract

        When there are only weak references for object accessibility, the object will be recycled when the system triggers gc

 //In this case, the thread has ended and can be cleared or not
        UserRuntimeEnv.clear();
//ThreadLocalMap source code
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.

            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();
        }

In the above example, you can see that the ThreadLocal object can be cleared or not. Will it not cause memory leakage if it is not cleared? No, because ThreadLocalMap has a recycling mechanism. It can be seen from the ThreadLocalMap source code that when value is set, the entry object will be retrieved and the object with null key will be cleared

That is, when the key is null, the object will be recycled automatically,

Then why is the key null?

This refers to the weak reference definition just mentioned. When an object is only pointed to by a weak reference, it will be automatically recycled in gc. If the key object is only pointed to by a weak reference, it will become null after gc

// ThreadLocalMap source code
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

From ThreadLocalMap   It can be seen from the source code that, as shown in the figure above, the dotted line is a weak reference. During thread execution, the key points to ThreadLocal is a weak reference, the threadLocalRef (reference of userId) points to ThreadLocal is a strong reference. When the thread execution is completed, the reference in the thread stack is cleared, and there are only weak reference points left in ThreadLocal, which will be recycled after gc, and the corresponding value will be recycled later

Introduction to weak references

        //Create two objects pointed to by strong references
        Object objA=new Object();
        Object objB=new Object();

        //Create two weak references weakA and weakB and one strong reference strongA;
        WeakReference<Object> weakA=new WeakReference<>(objA);
        WeakReference<Object>weakB=new WeakReference<>(objB);
        Object strongA=objA;

At this time, the reference of objA and objB is cancelled  

        objA=null;
        objB=null;

 

After executing gc

 System.gc();

 

After execution, Object@0002 Objects are recycled. Note that gc recycles objects in heap memory, not reference objects

  If there are mistakes, thank you for correction, communicate with each other and make common progress

 

Posted by kundan on Tue, 30 Nov 2021 02:33:09 -0800