【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【防止内存泄漏】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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();