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