一.ThreadLocal线程变量的实现原理
1.ThreadLocal核心方法有这个几个
get()、set(value)、remove()
2.实现原理
ThreadLocal在每个线程都会创建一个线程内对应的T的副本,本T数据可以在本线程内任何地方可以被使用。线程之间互相不影响,所以是线程安全的。
3.底层结构
ThreadLocal实现各个线程数据副本的存取,是通过操作它的内部类ThreadLocalMap,进行键值对的存取和移除。
4.set(value)方法的底层
public voidset(T value) {
Thread t=Thread.currentThread();
ThreadLocalMap map=getMap(t);if (map != null)
map.set(this, value);elsecreateMap(t, value);
}
voidcreateMap(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]= newEntry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >=threshold)
rehash();
}
set(value)
1》根据当前线程,获取本线程所拥有的TreadLocalMap,如果没有,则创建一个新的。
2》ThreadLocalMap的即。【这里的ThreadLocal对象在set处,是根据本对象的hashCode经过计算获取到下标,然后循环对比Entry[]中每一个Entry的key进行插入或覆盖操作】
3》那么可以看出结构是:
3.1》每一个Thread有一个对应的ThreadLocalMap。Map的即
3.2》set操作根据ThreadLocal对象的hashCode对比Entry[]数组,进行新增插入或覆盖操作。
5.get()方法底层
publicT 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;returnresult;
}
}returnsetInitialValue();
}
private Entry getEntry(ThreadLocal>key) {int i = key.threadLocalHashCode & (table.length - 1);
Entry e=table[i];if (e != null && e.get() ==key)returne;else
returngetEntryAfterMiss(key, i, e);
}
privateT setInitialValue() {
T value=initialValue();
Thread t=Thread.currentThread();
ThreadLocalMap map=getMap(t);if (map != null)
map.set(this, value);elsecreateMap(t, value);returnvalue;
}
protectedT initialValue() {return null;
}
get()
1》根据当前Thread线程,获取本线程的ThreadLocalMap
2》然后将键,也就是本ThreadLocal作为键传入,从Map中获取value。【获取的过程即,根据ThreadLocal对象的hashCode经过计算获取下标,根据下标取出Entry[]数组中的具体值,返回结果】
3》如果没有值,则返回null。
6.remove()底层
public voidremove() {
ThreadLocalMap m=getMap(Thread.currentThread());if (m != null)
m.remove(this);
}
remove()
1》本线程结束以前,一定要调用remove,清除线程变量中本次的变量。防止内存泄漏
二.ThreadLocal使用场景
拦截器存储 调用接口的用户信息,在本次Request到达,处理,直到返回的本线程中,都可以使用线程变量中的用户信息。
1.定义线程变量
public classRequestData {//线程变量 租户对象
public static final ThreadLocal TENEMENT_USER = new ThreadLocal();
2.到达controller之前的拦截器中,赋值线程变量。request返回之前remove【防止内存泄漏】
importjava.net.URLDecoder;importjava.net.URLEncoder;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.apache.commons.lang3.StringUtils;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importcom.alibaba.fastjson.JSON;importcom.pisen.cloud.luna.core.enums.LunaHeaderNames;importcom.pisen.cloud.luna.core.interceptor.utils.LunaInterceptorUtil;importcom.pisen.cloud.luna.core.reqmodal.RequestData;importcom.pisen.cloud.luna.core.utils.TenementUser;public class TenementAuthinterceptor implementsHandlerInterceptor{
@Overridepublic booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException {
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;
}
}
@Overridepublic voidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView)throwsException {
RequestData.TENEMENT_USER.remove();
}
@Overridepublic voidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throwsException {
}
}
View Code
3.controller中使用线程变量
//创建单据
@RequestMapping(value = "/insert",method =RequestMethod.POST)public AjaxResultinsert(@RequestBody SaleBill bill){
TenementUser tuser= RequestData.TENEMENT_USER.get();