【java】ThreadLocal线程变量的实现原理和使用场景
一.ThreadLocal线程变量的实现原理
1.ThreadLocal核心方法有这个几个
get()、set(value)、remove()
2.实现原理
ThreadLocal在每个线程都会创建一个线程内对应的T的副本,本T数据可以在本线程内任何地方可以被使用。线程之间互相不影响,所以是线程安全的。
3.底层结构
ThreadLocal实现各个线程数据副本的存取,是通过操作它的内部类ThreadLocalMap,进行<k,v>键值对的存取和移除。
4.set(value)方法的底层
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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();
}
set(value)
1》根据当前线程,获取本线程所拥有的TreadLocalMap,如果没有,则创建一个新的。
2》ThreadLocalMap的<key,value>即<ThreadLocal对象,传入的value值>。【这里的ThreadLocal对象在set处,是根据本对象的hashCode经过计算获取到下标,然后循环对比Entry[]中每一个Entry的key进行插入或覆盖操作】
3》那么可以看出结构是:
3.1》每一个Thread有一个对应的ThreadLocalMap。Map的<K,V>即<当前ThreadLocal对象,传入的value>
3.2》set操作根据ThreadLocal对象的hashCode对比Entry[]数组,进行新增插入或覆盖操作。
5.get()方法底层
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();
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
get()
1》根据当前Thread线程,获取本线程的ThreadLocalMap
2》然后将<K>键,也就是本ThreadLocal作为键传入,从Map中获取value。【获取的过程即,根据ThreadLocal对象的hashCode经过计算获取下标,根据下标取出Entry[]数组中的具体值,返回结果】
3》如果没有值,则返回null。
6.remove()底层
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
remove()
1》本线程结束以前,一定要调用remove,清除线程变量中本次的变量。防止内存泄漏
二.ThreadLocal使用场景
拦截器存储 调用接口的用户信息,在本次Request到达,处理,直到返回的本线程中,都可以使用线程变量中的用户信息。
1.定义线程变量
public class RequestData {
//线程变量 租户对象
public static final ThreadLocal<TenementUser> TENEMENT_USER = new ThreadLocal<TenementUser>();
2.到达controller之前的拦截器中,赋值线程变量。request返回之前remove【防止内存泄漏】
import java.net.URLDecoder;
import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON;
import com.pisen.cloud.luna.core.enums.LunaHeaderNames;
import com.pisen.cloud.luna.core.interceptor.utils.LunaInterceptorUtil;
import com.pisen.cloud.luna.core.reqmodal.RequestData;
import com.pisen.cloud.luna.core.utils.TenementUser; public class TenementAuthinterceptor implements HandlerInterceptor{ @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { String tenInfo = request.getHeader(LunaHeaderNames.TENEMENT_INFO.getName());
TenementUser tuser = null; if(StringUtils.isNotBlank(tenInfo)){ try {
tenInfo = URLDecoder.decode(tenInfo, "UTF-8");
tuser = JSON.parseObject(tenInfo,TenementUser.class);
if(tuser != null){ if(StringUtils.isBlank(tuser.getTenementId()) || StringUtils.isBlank(tuser.getLoginName())){
tuser = null;
}else{
RequestData.TENEMENT_USER.set(tuser);
}
} } catch (Exception e) {
e.printStackTrace();
}
} if(tuser != null){
return true;
}else{ String errorMsg = "登录失败,请重新登录!";
LunaInterceptorUtil.ErrorResp(response,errorMsg);
return false;
} } @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
RequestData.TENEMENT_USER.remove();
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
} }
3.controller中使用线程变量
//创建单据
@RequestMapping(value = "/insert",method = RequestMethod.POST)
public AjaxResult<SaleBill> insert(@RequestBody SaleBill bill){ TenementUser tuser = RequestData.TENEMENT_USER.get();
【java】ThreadLocal线程变量的实现原理和使用场景的更多相关文章
- 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)
简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...
- java ThreadLocal线程设置私有变量底层源码分析
前面也听说了ThreadLocal来实现高并发,以前都是用锁来实现,看了挺多资料的,发现其实还是区别挺大的(感觉严格来说ThreadLocal并不算高并发的解决方案),现在总结一下吧. 高并发中会出现 ...
- Java中线程池的实现原理
知识点总结 ---------------------------------------------------------------------------------------------- ...
- Java中线程池的实现原理-求职必备
jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行.被哪个线程执行,以及什么时候执行. 初始化线程池(4种) ...
- 线程池;java的线程池的实现原理;适用于频繁互动(如电商网站)
线程池是一种多线程处理形式,处理过程中将任务加入到队列,然后在创建线程后自己主动启动这些任务.线程池线程都是后台线程.每一个线程都使用默认的堆栈大小,以默认的优先级执行.并处于多线程单元中. 假设某个 ...
- 线程变量ThreadLocal的使用
我们有时候会通过token进行多次查询(猪:token是redis中的key),比如: 一次是在登录拦截器中,一次是在controller的业务中查询,这样存在性能和资源的浪费问题!!! 那么如何将拦 ...
- Java多线程——线程间通信
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...
- 线程池续:你必须要知道的线程池submit()实现原理之FutureTask!
前言 上一篇内容写了Java中线程池的实现原理及源码分析,说好的是实实在在的大满足,想通过一篇文章让大家对线程池有个透彻的了解,但是文章写完总觉得还缺点什么? 上篇文章只提到线程提交的execute( ...
- Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理
1.什么是ThreadLocal ThreadLocal顾名思义是线程局部变量.这种变量和普通的变量不同,这种变量在每个线程中通过get和set方法访问, 每个线程有自己独立的变量副本.线程局部变量不 ...
随机推荐
- centos:SSH登录时间很慢
vi /etc/ssh/sshd_config GSSAPIAuthentication 改为 no 开启UseDNS,值改为 no service sshd restart
- Coursera台大机器学习技法课程笔记10-Random forest
随机森林就是要将这我们之前学的两个算法进行结合:bagging能减少variance(通过g们投票),而decision tree的variance很大,资料不同,生成的树也不同. 为了得到不同的g, ...
- Explain之key_len长度计算
以前我很好奇通过执行计划Explain去分析SQL的时候看到的key_len值有时很小,有时看到很大,那时也不知道它是怎么算出来的,现在终于搞懂了,嘻.因为网上对key_len的长度的计算的资料也很少 ...
- Smashing The Browser:From Vulnerability Discovery To Exploit学习记录
浏览器Fuzz技术 漏洞挖掘 白盒挖掘 代码审计 自动化代码分析 黑盒挖掘 Fuzzing 两种Fuzzing技术 静态Fuzzing 基于变异的 文件.文档 多媒体 bf3 基于生成的 浏览器 重点 ...
- .NetCore中结合ExceptionLess的处理对Polly再次封装
/// <summary> /// Polly封装 liyouming /// </summary> public class PollyServicesFactory { p ...
- Polly公共处理 -重试(Retry)
封装处理下Polly重试 private ILogger<PollyHelper> _logger; /// <summary> /// /// </summary> ...
- sqrt
sqrt特别慢,两边同时平方,尽量避免开方.
- WIN10下 VS2017+OpenCv 3.4.1 配置
写篇博客来记录一下opencv在VS中的配置. 一.下载OpenCv安装包 下载的途径有三种: 1.官网下载 但是官网下载真的是贼头大,首先下载好好的突然说下载中断,而且无法恢复,此外,还慢,毕竟外网 ...
- Eclipse从SVN检出maven项目后的一些配置
Eclipse从SVN检出maven项目后,会发现它只是一个普通的java project,如图: 这里我们需要把它转成maven-webapp,并调整相关属性,设置运行环境,关联相关jar目录等. ...
- 2,EasyNetQ-日志记录
EasyNetQ提供了一个IEasyNetQLogger接口: public interface IEasyNetQLogger { void DebugWrite(string format, pa ...