【java】ThreadLocal线程变量的实现原理和使用场景

时间:2021-04-10 05:29:20

一.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();