一.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线程变量的实现原理和使用场景的更多相关文章

  1. 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)

    简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...

  2. java ThreadLocal线程设置私有变量底层源码分析

    前面也听说了ThreadLocal来实现高并发,以前都是用锁来实现,看了挺多资料的,发现其实还是区别挺大的(感觉严格来说ThreadLocal并不算高并发的解决方案),现在总结一下吧. 高并发中会出现 ...

  3. Java中线程池的实现原理

    知识点总结 ---------------------------------------------------------------------------------------------- ...

  4. Java中线程池的实现原理-求职必备

    jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行.被哪个线程执行,以及什么时候执行. 初始化线程池(4种) ...

  5. 线程池;java的线程池的实现原理;适用于频繁互动(如电商网站)

    线程池是一种多线程处理形式,处理过程中将任务加入到队列,然后在创建线程后自己主动启动这些任务.线程池线程都是后台线程.每一个线程都使用默认的堆栈大小,以默认的优先级执行.并处于多线程单元中. 假设某个 ...

  6. 线程变量ThreadLocal的使用

    我们有时候会通过token进行多次查询(猪:token是redis中的key),比如: 一次是在登录拦截器中,一次是在controller的业务中查询,这样存在性能和资源的浪费问题!!! 那么如何将拦 ...

  7. Java多线程——线程间通信

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  8. 线程池续:你必须要知道的线程池submit()实现原理之FutureTask!

    前言 上一篇内容写了Java中线程池的实现原理及源码分析,说好的是实实在在的大满足,想通过一篇文章让大家对线程池有个透彻的了解,但是文章写完总觉得还缺点什么? 上篇文章只提到线程提交的execute( ...

  9. Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理

    1.什么是ThreadLocal ThreadLocal顾名思义是线程局部变量.这种变量和普通的变量不同,这种变量在每个线程中通过get和set方法访问, 每个线程有自己独立的变量副本.线程局部变量不 ...

随机推荐

  1. Python_oldboy_自动化运维之路_paramiko,mysql(十二)

    本节内容: paramiko mysql 1.paramiko http://www.cnblogs.com/wupeiqi/articles/5095821.html paramiko是一个模块,s ...

  2. 模板引擎--hogan

    学习地址:https://www.imooc.com/article/18493 Hogan的github地址镇楼:https://github.com/twitter/hogan.js

  3. SysV和BSD启动风格的比较

    Slackware启动脚本与System V启动脚本的区别何在? Slackware 使用BSD风格的init脚本,而很多别的发行版使用System V风格的init脚本.SysV和BSD脚本都是能让 ...

  4. MFC命名规范

    属性部分 全局变量:g_ 常量:c_ c++类成员变量:m_ 静态变量:s_ 类型部分 指针:p 函数:fn 无效:v 句柄:h 长整型:l 布尔:b 浮点型(有时也指文件):f 双字:dw 字符串: ...

  5. MAC下代理工具Charles使用

    一.跟踪HTTPS 1.下载官方的证书ssl.zip证书,解压成*.crt 2.可以通过邮箱或者发布到自己的服务器的方式,然后用手机去下载安装crt文件. 3.charles设置Proxy--> ...

  6. Mysql报Cannot load from mysql.proc. The table is probably corrupted

    1548-Cannot load from mysql.proc. The table is probably corrupted http://bugs.mysql.com/bug.php?id=5 ...

  7. 【深入Struts2】获取ServletAPI的三种方式

    一:获取servletAPI的三种方法 在传统的Web开发中,经常会用到Servlet API中的HttpServletRequest.HttpSession和ServletContext.Strut ...

  8. node.js之nodemon 代码热更新 修改代码后服务器自动重启

    1.安装nodemon: npm install -g nodemon //全局安装 npm install nodemon --save //局部安装 2.在项目根目录下创建 nodemon.jso ...

  9. [js]BOM篇

    一.什么是BOM BOM(Browser Object Model)即浏览器对象模型. BOM提供了独立于内容 而与浏览器窗口进行交互的对象: 由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对 ...

  10. 100BASE-TX / 100BASE-T4/100BASE-FX

    IEEE标准共有以下几种:10BASE-5:粗缆.最大传输距离500米,使用AUI连接器连接或使用收发器电缆和收发器(MAU)进行连接.10BASE-2:细缆.实际传输距离为185米,使用BNC连接器 ...