一. 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 {
- }
- }
- View Code
3.controller 中使用线程变量
- // 创建单据
- @RequestMapping(value = "/insert",method = RequestMethod.POST)
- public AjaxResult<SaleBill> insert(@RequestBody SaleBill bill){
- TenementUser tuser = RequestData.TENEMENT_USER.get();
来源: http://www.bubuko.com/infodetail-2968452.html