当前位置:网站首页>Principle and application of ThreadLocal
Principle and application of ThreadLocal
2022-07-04 18:44:00 【What is happiness】
ThreadLocal Principle and use
Recently, I saw a piece of code in the system management component of the company to obtain personnel information :
// Get login user information
UserLoginInfo userLoginInfo = ThreadLocalUtils.getUserLoginInfo();
getUserLoginInfo() The method was originally thought to be through token Parse or pass token check redis. But when I click in, I find that it's from ThreadLocal Get login information in ?
private static final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);
public static UserLoginInfo getUserLoginInfo() {
return get(Constants.USER_LOGIN_INFO);
}
/** * from ThreadLocal Get the value * * @param key key * @param <T> Value generics * @return value , Returns if it does not exist null, If the type is inconsistent with the generic , May throw out {@link ClassCastException} * @see Map#get(Object) * @see ClassCastException */
public static <T> T get(String key) {
return ((T) local.get().get(key));
}
After some study, baidu found Threadlocal Data isolation between threads . At this time, I think of what I learned before volatile Ensure the visibility of data variables . Why is data isolated and visible . What's the difference ? Can't help being confused , The following is a personal comb
volatile
volatile yes Java One of the keywords of , A shared variable ( Member variables of class 、 Static member variable of class ) By volatile After modification , It has two layers of semantics : It ensures the visibility of different threads when operating on this variable and prohibits instruction reordering .
Java Medium variables are stored in main memory , Threads cannot directly read and manipulate variables in main memory , Instead, variables will be read into working memory for operation . and volatile The function of is to ensure that all threads can get the latest value of variables at the first time . But here's the thing , Yes volatile Modification of variables is not atomic . Because the change operation includes reading variables , Modify variables and write to working memory . and volatile It's just a guarantee that The read variable is the latest value read . therefore ,volatile There is no guarantee that any operation on variables is atomic .
ThreadLocal
ThreadLocal A separate copy of the variable is provided for each thread that uses the variable , Ensure the isolation between threads . Explore the bottom ,ThreadLocal Is through static inner classes ThreadLocalMap Realized .
Here is ThreadLocal Class diagram structure of , It can be seen from the picture that :Thread There are two variables in the class threadLocals and inheritableThreadLocals, Both ThreadLocal Inner class ThreadLocalMap Variable of type , We look inside ThreadLocalMap It can be found that it is actually similar to a HashMap
Different from what I thought , The local variables of each thread are not stored in ThreadLocal In the example , Instead, it is placed on the of the calling thread ThreadLocals In variables ( I said that before , The variable is Thread The class variables ). in other words ,ThreadLocal Local variables of type are stored in specific thread space , It is itself equivalent to a tool shell for loading local variables , adopt set Methods will value Added to the calling thread threadLocals in , When the calling thread calls get Methods can be learned from its threadLocals Take variables out of .
set Method
public void set(T value) {
//(1) Get the current thread ( Caller thread )
Thread t = Thread.currentThread();
//(2) Take the current thread as the key value , To find the corresponding thread variable , Find the corresponding map
ThreadLocalMap map = getMap(t);
//(3) If map Not for null, Add local variables directly ,key For the currently defined ThreadLocal Variable this quote , The value is the added local variable value
if (map != null)
map.set(this, value);
//(4) If map by null, Description first added , You need to create the corresponding map
else
createMap(t, value);
}
In the code above ,(2) In the call getMap Method to obtain the corresponding of the current thread threadLocals( Refer to the above illustration and text description ), The method code is as follows
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; // Get the thread's own variables threadLocals, And bind to the member variables of the current calling thread. threadLocals On
}
If the getMap Method return value is not null, Just put value The value is set to threadLocals in (key Reference for current thread , The value is a local variable ); If getMap Method returns null Description is the first call set Method ( We talked about that earlier ,threadLocals The default value is null, Only a call set Method is created map), At this point, you need to call createMap Method creation threadLocals, The method is as follows
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
createMap Method not only creates threadLocals, The local variable value to be added is also added to the threadLocals in .
get Method
stay get Method implementation , First, get the current caller thread. , If the current thread's threadLocals Not for null, Directly return the local variable value bound by the current thread , Otherwise execution setInitialValue Method initialization threadLocals Variable . stay setInitialValue In the method , Be similar to set Method implementation , Are used to judge the current thread threadLocals Is the variable null, Yes, add local variables ( At this time, due to initialization , So the added value is null), Otherwise create threadLocals Variable , Similarly, the added value is null
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
threadLocal and threadLocalMap The relationship is shown in the following figure .
threadlocal Memory leak problem
As we can see in the picture above ,threadLocal In the end, it's called threadlocalMap Class method , and threadlocalMap The interior of is actually a Entry Array
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
At present ThreadLocal References to k Be passed on to WeakReference Constructor for , therefore ThreadLocalMap Medium key yes ThreadLocal The weak references . If key threadlocal by null 了 , This entry It's cleared .
When threadlocal by null Or after being recycled , but ThreadLocalMap(threadlocals) Is the internal property of the thread , Its life cycle is synchronized with its thread . It won't be recycled . So at this time ThreadLocalMap(threadlocals) There will be a phenomenon :key It's empty , however value The value is still there . This is the memory leak problem .
Avoid it
Calling ThreadLocal Finally, we must call remove() Method .
Then the article begins with threadLocal Get user login information in . Found an interceptor in the project , Every request will token The user information obtained in is stored in threadLocal in . It's like this service Layer code can be passed directly threadlocal obtain , You don't need to pass... Through parameters .
/** * @author LSWXXY-WQ */
@Slf4j
public class UserInterceptor extends HandlerInterceptorAdapter {
@Autowired
UserService userService;
@Autowired
MenuService menuService;
@Resource
private AuthorizationService authorizationService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// obtain token
String token = request.getHeader(Constants.USER_TOKEN_HEADER);
// adopt token Get user information and put it in ThreadLocal in
if (StringUtil.isBlank(token) ) {
UserloginInfo userloginInfo = userService.getLoginInfo(token)
ThreadLocalUtils.setUserLoginInfo(userloginInfo);
}
// A series of subsequent verifications, etc
.....
.....
.....
.....
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Empty after business threadlocal
ThreadLocalUtils.clear();
super.afterCompletion(request, response, handler, ex);
}
At the end of the paper, the of the project is attached threadLocalUtils Well . The core is still right ThreadLocal Of get、set Use
public final class ThreadLocalUtils {
private ThreadLocalUtils() {
}
private static final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);
/** * @return threadLocal All values in */
public static Map<String, Object> getAll() {
return local.get();
}
/** * Set a value to ThreadLocal * * @param key key * @param value value * @param <T> The type of value * @return The value put * @see Map#put(Object, Object) */
public static <T> T put(String key, T value) {
local.get().put(key, value);
return value;
}
/** * Delete the value corresponding to the parameter * * @param key * @see Map#remove(Object) */
public static void remove(String key) {
local.get().remove(key);
}
/** * Empty ThreadLocal * * @see Map#clear() */
public static void clear() {
local.remove();
}
/** * from ThreadLocal Get the value * * @param key key * @param <T> Value generics * @return value , Returns if it does not exist null, If the type is inconsistent with the generic , May throw out {@link ClassCastException} * @see Map#get(Object) * @see ClassCastException */
public static <T> T get(String key) {
return ((T) local.get().get(key));
}
/** * from ThreadLocal Get the value , And specify a provider when the value does not exist * * @see Supplier * @since 3.0 */
public static <T> T get(String key, Supplier<T> supplierOnNull) {
return ((T) local.get().computeIfAbsent(key, k -> supplierOnNull.get()));
}
/** * Get a value and delete * * @param key key * @param <T> Value type * @return value , Returns if it does not exist null * @see this#get(String) * @see this#remove(String) */
public static <T> T getAndRemove(String key) {
try {
return get(key);
} finally {
remove(key);
}
}
public static void setUserLoginInfo(UserLoginInfo loginInfo) {
put(Constants.USER_LOGIN_INFO, loginInfo);
}
public static UserLoginInfo getUserLoginInfo() {
return get(Constants.USER_LOGIN_INFO);
}
public static void setAppLoginInfo(AppLoginInfo loginInfo) {
put(Constants.APP_LOGIN_INFO, loginInfo);
}
public static AppLoginInfo getAppLoginInfo() {
return get(Constants.APP_LOGIN_INFO);
}
public static void setAppName(String appName) {
put(Constants.APP_NAME, appName);
}
public static String getAppName() {
return get(Constants.APP_NAME);
}
public static void setSysLog(SysLog sysLog){
put(Constants.OPERATE_LOG,sysLog);
}
public static SysLog getSysLog(){
return get(Constants.OPERATE_LOG);
}
}
边栏推荐
- 中国农科院基因组所汪鸿儒课题组诚邀加入
- 同事悄悄告诉我,飞书通知还能这样玩
- 2022年字节跳动日常实习面经(抖音)
- How to download files using WGet and curl
- Once the "king of color TV", he sold pork before delisting
- 【2022年江西省研究生数学建模】水汽过饱和的核化除霾 思路分析及代码实现
- Open source PostgreSQL extension age for graph database was announced as the top-level project of Apache Software Foundation
- Tutorial on the use of Huawei cloud modelarts (with detailed illustrations)
- 字节跳动Dev Better技术沙龙成功举办,携手华泰分享Web研发效能提升经验
- MySQL常用增删改查操作(CRUD)
猜你喜欢
随机推荐
The controversial line of energy replenishment: will fast charging lead to reunification?
What if the self incrementing ID of online MySQL is exhausted?
ESP32-C3入门教程 问题篇⑫——undefined reference to rom_temp_to_power, in function phy_get_romfunc_addr
Behind the ultra clear image quality of NBA Live Broadcast: an in-depth interpretation of Alibaba cloud video cloud "narrowband HD 2.0" technology
Redis主从复制
Wireshark抓包TLS协议栏显示版本不一致问题
2022 national CMMI certification subsidy policy | Changxu consulting
提升复杂场景三维重建精度 | 基于PaddleSeg分割无人机遥感影像
同事悄悄告诉我,飞书通知还能这样玩
[HCIA continuous update] WAN technology
Scala基础教程--16--泛型
Microservice architecture debate between radical technologists vs Project conservatives
字节跳动Dev Better技术沙龙成功举办,携手华泰分享Web研发效能提升经验
Five thousand words to clarify team self-organization construction | Liga wonderful talk
LD_LIBRARY_PATH 环境变量设置
How to modify icons in VBS or VBE
Grain Mall (I)
Nature Microbiology | 可感染阿斯加德古菌的六种深海沉积物中的病毒基因组
File processing examples of fopen, FREAD, fwrite, fseek
[system disk back to U disk] record the operation of system disk back to U disk