privatestatic ThreadLocal<MemberRespVo> memberRespVoContext = new ThreadLocal<>();
// Set the user information if it not null. publicstaticvoidsetCurrentUser(MemberRespVo memberRespVo){ // ThreadLocal is thread-local variable, therefore it hasn't concurrent problem. // Each thread call the threadLocal.get is get different result. if (memberRespVoContext.get() == null) { memberRespVoContext.set(memberRespVo); } } // Get current user information. publicstatic MemberRespVo getCurrentUser(){ return memberRespVoContext.get(); }
// Remove current user information from ThreadLocal. publicstaticvoidremoveCurrentUser(){ memberRespVoContext.remove(); } }
/** * Remove ther user infomation in ThreadLocal when complete of request processing and view rendering. * Why not remove in postHandle ? * https://stackoverflow.com/questions/37358426/remove-threadlocal-object-within-a-spring-mvc-website * The postHandle method looks like a good place, but afterCompletion is better because it's called * even when the handler fails to process the request correctly (aka an exception occurred). */ @Override publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception { UserContextHolder.removeCurrentUser(); } }
privatestaticfinal ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<>("Request attributes");
privatestaticfinal ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<>("Request context");
/** * Reset the RequestAttributes for the current thread. */ publicstaticvoidresetRequestAttributes(){ requestAttributesHolder.remove(); inheritableRequestAttributesHolder.remove(); }
/** * Bind the given RequestAttributes to the current thread, */ publicstaticvoidsetRequestAttributes(@Nullable RequestAttributes attributes){ setRequestAttributes(attributes, false); }
/** * Bind the given RequestAttributes to the current thread. */ publicstaticvoidsetRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable){ if (attributes == null) { resetRequestAttributes(); } else { if (inheritable) { inheritableRequestAttributesHolder.set(attributes); requestAttributesHolder.remove(); } else { requestAttributesHolder.set(attributes); inheritableRequestAttributesHolder.remove(); } } }
/** * Return the RequestAttributes currently bound to the thread. */ @Nullable publicstatic RequestAttributes getRequestAttributes(){ RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; }
/** * Return the RequestAttributes currently bound to the thread. */ publicstatic RequestAttributes currentRequestAttributes()throws IllegalStateException { RequestAttributes attributes = getRequestAttributes(); if (attributes == null) { if (jsfPresent) { attributes = FacesRequestAttributesFactory.getFacesRequestAttributes(); } if (attributes == null) { thrownew IllegalStateException("No thread-bound request found..."); } } return attributes; } // ... }
This number represents the golden ratio (sqrt(5)-1) times two to the power of 31 ((sqrt(5)-1) * (2^31)). The result is then a golden number, either 2654435769 or -1640531527.
// 在遇到空插槽之前,找不到匹配的ThreadLocal对象。将ThreadLocal对象和缓存的值排列在空插槽中 tab[i] = new Entry(key, value); int sz = ++size; // 如果没有元素被清理,检查当前数组的容量是否差错扩展临界值,以此来考虑是否需要扩展。 if (!cleanSomeSlots(i, sz) && sz >= threshold) { rehash(); } }
下图展示了Thread、ThreadLocal、ThreadLocalMap三者之间的关系:
ThreadLocal的get方法
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public T get(){ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { // 尝试从map中获取Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T) e.value; return result; } } // 如果map为null,初始化当前线程的ThreadLocalMap,最后返回与当前ThreadLocal对象关联的初始值 return setInitialValue(); }
执行的时序图如下:
ThreadLocalMap.getEntry方法:
1 2 3 4 5 6 7 8 9 10 11 12
private Entry getEntry(ThreadLocal<?> key){ // 运算获取下标 int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; // 如果e不为空,而且e对一个拿到key与传入的相同,表示正是要找的,直接返回 if (e != null && e.get() == key) { return e; } else { // 如果key不相等或者e为null,从i开始先后探索查找 return getEntryAfterMiss(key, i, e); } }