【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【防止内存泄漏】
- 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线程变量的实现原理和使用场景的更多相关文章
- 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)
简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...
- java ThreadLocal线程设置私有变量底层源码分析
前面也听说了ThreadLocal来实现高并发,以前都是用锁来实现,看了挺多资料的,发现其实还是区别挺大的(感觉严格来说ThreadLocal并不算高并发的解决方案),现在总结一下吧. 高并发中会出现 ...
- Java中线程池的实现原理
知识点总结 ---------------------------------------------------------------------------------------------- ...
- Java中线程池的实现原理-求职必备
jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行.被哪个线程执行,以及什么时候执行. 初始化线程池(4种) ...
- 线程池;java的线程池的实现原理;适用于频繁互动(如电商网站)
线程池是一种多线程处理形式,处理过程中将任务加入到队列,然后在创建线程后自己主动启动这些任务.线程池线程都是后台线程.每一个线程都使用默认的堆栈大小,以默认的优先级执行.并处于多线程单元中. 假设某个 ...
- 线程变量ThreadLocal的使用
我们有时候会通过token进行多次查询(猪:token是redis中的key),比如: 一次是在登录拦截器中,一次是在controller的业务中查询,这样存在性能和资源的浪费问题!!! 那么如何将拦 ...
- Java多线程——线程间通信
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...
- 线程池续:你必须要知道的线程池submit()实现原理之FutureTask!
前言 上一篇内容写了Java中线程池的实现原理及源码分析,说好的是实实在在的大满足,想通过一篇文章让大家对线程池有个透彻的了解,但是文章写完总觉得还缺点什么? 上篇文章只提到线程提交的execute( ...
- Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理
1.什么是ThreadLocal ThreadLocal顾名思义是线程局部变量.这种变量和普通的变量不同,这种变量在每个线程中通过get和set方法访问, 每个线程有自己独立的变量副本.线程局部变量不 ...
随机推荐
- java虚拟机规范(se8)——java虚拟机结构(一)
本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第二章 虚拟机结构 本文档描述了一个抽象的虚拟机规范,并不描述 ...
- KNN算法的感受 1
本来预计的打算是一天一个十大挖掘算法,然而由于同时要兼顾数据结构面试的事情,所以 很难办到,但至少在回家前要把数据挖掘十大算法看完,过个好年,在course上学习老吴的课程还是帮了我很大的忙,虽然浪费 ...
- knnMatch
先马克下,回头再看:http://blog.csdn.net/zkl99999/article/details/47950425 http://blog.csdn.net/yangtrees/arti ...
- High-Speed Tracking with Kernelized Correlation Filters
2015年的一篇论文,可参考:http://blog.csdn.net/carrierlxksuper/article/details/46461245. 另参考:http:// ...
- html圈圈
<html> <head> <meta charset="utf-8" /> <link href="images/style. ...
- ERP渠道信息的修改和渠道联系记录(二十三)
用例图: 前端代码: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Cha ...
- k8s的imagePullSecrets如何生成及使用
如果公司的docker仓库(harbor),需要用户认证之后,才能拉取镜像. 那如何在k8s里生成这个secret呢? 这个secret如何还原呢? 在k8s的yaml文件里如何实现呢? 这里提供几个 ...
- 【LOJ】#121. 「离线可过」动态图连通性
题解 和BZOJ4025挺像的 就是维护边权是时间的最大生成树 删边直接删 两点未联通时直接相连,两点联通则找两点间边权小的一条边删除即可 代码 #include <bits/stdc++.h& ...
- Codeforces Round #296 (Div. 1) B - Clique Problem
B - Clique Problem 题目大意:给你坐标轴上n个点,每个点的权值为wi,两个点之间有边当且仅当 |xi - xj| >= wi + wj, 问你两两之间都有边的最大点集的大小. ...
- King Arthur's Birthday Celebration
每天抛一个硬币,硬币正面朝上的几率是p,直到抛出k次正面为止结束,第一天抛硬币需花费1,第二天花费3,然后是5,7,9……以此类推,让我们求出抛硬币的天数的期望和花费的期望. 天数期望: A.投出了k ...