【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作为键传入,获取value。【获取的过程即,根据ThreadLocal对象的hashCode经过计算获取下标,根据下标取出Entry[]数组中的具体值,返回结果】

  3》如果没有值,则返回null。

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

原文地址:https://www.cnblogs.com/sxdcgaq8080/p/10436495.html

时间: 2024-11-09 16:23:04

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

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

前面也听说了ThreadLocal来实现高并发,以前都是用锁来实现,看了挺多资料的,发现其实还是区别挺大的(感觉严格来说ThreadLocal并不算高并发的解决方案),现在总结一下吧. 高并发中会出现的问题就是线程安全问题,可以说是多个线程对共享资源访问如何处理的问题,处理不当会的话,会出现结果和预期会完全不同. 一般情况下,多个线程访问一个变量都是公用他们的值,不过有时候虽然也是访问共享变量,不过每个线程却需要自己的私有变量.这个时候ThreadLocal就有用武之地了.下面是个ThreadL

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

线程池是一种多线程处理形式,处理过程中将任务加入到队列,然后在创建线程后自己主动启动这些任务.线程池线程都是后台线程.每一个线程都使用默认的堆栈大小,以默认的优先级执行.并处于多线程单元中. 假设某个线程在托管代码中空暇(如正在等待某个事件),则线程池将插入还有一个辅助线程来使全部处理器保持繁忙. 假设全部线程池线程都始终保持繁忙,但队列中包括挂起的工作,则线程池将在一段时间后创建还有一个辅助线程但线程的数目永远不会超过最大值.超过最大值的线程能够排队,但他们要等到其它线程完毕后才启动. 组成部

线程变量ThreadLocal的使用

我们有时候会通过token进行多次查询(猪:token是redis中的key),比如: 一次是在登录拦截器中,一次是在controller的业务中查询,这样存在性能和资源的浪费问题!!! 那么如何将拦截器中的数据传递到Controller中呢? 有两种方案: 1,将User对象放置到request对象中 2,使用ThreadLocal线程变量实现(在进入tomcat和产生响应前,对象都处于同一个线程中) 实现: 1,定义一个ThreadLocal相关的类 public class UserThr

Java并发机制(4)--ThreadLocal线程本地变量(转)

转自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html Java并发编程:深入剖析ThreadLocal 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal

Java中线程封闭之ThreadLocal

在访问共享数据时通常使用同步.若不使用同步则可以将对象封闭在一个线程中达到线程安全的目的,该方法称为线程封闭(Thread Confinement).其中实现线程封闭中规范的方法是使用ThreadLocal类.线程封闭技术一种常用的使用场景是在JDBC Connection对象. public class ConnectionHelper{private final static String URL = "";private final static ThreadLocal<C

从零開始学Java之线程具体解释(1):原理、创建

Java线程:概念与原理 一.操作系统中线程和进程的概念 如今的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中执行的应用程序.每一个进程都有自己独立的一块内存空间.一个进程中能够启动多个线程. 比方在Windows系统中.一个执行的exe就是一个进程. 线程是指进程中的一个执行流程.一个进程中能够执行多个线程.比方java.exe进程中能够执行非常多线程. 线程总是属于某个进程,进程中的多个线程共享进程的内存. "同一时候"运行是人的感觉,在线程之间实际上

Java并发学习之九——使用本地线程变量

本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.如果创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味着,如果你在一个线程里改变一个属性,全部的线程都会受到这个改变的影响.如果希望程序里的哥哥线程的属性不会被共享,Java并发API提供了一个很清楚的机制叫本地线程变量. 2.Java并发API包括Inheritable ThreadLocal类提供线程创建线程的值的遗传性.如果线程A有一个本地线程变量,然

本地线程变量ThreadLocal (耗时工具)

本地线程变量类 package king; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; /** * TLTimeContainer为ThreadLocalTimeContainer(本地线程变量时间容器)的缩写 * 说明:用来在任意一个方法内部置入recordTime(),以作片断时间点的

Java中线程局部变量ThreadLocal使用教程及源码分析

在Java多线程编程中有时候会遇见线程本地局部变量ThreadLocal这个类,下面就来讲讲ThreadLocal的使用及源码分析. ThreadLocal 是Thread Local Varial(线程局部变量)的意思,每个线程在使用线程局部变量的时候都会为使用这个线程局部变量的线程提供一个线程局部变量的副本,使得每个线程都可以完全独立地操作这个线程局部变量,而不会与其他线程发生冲突,从线程的角度来看,每个线程都好像独立地拥有了这个线程局部变量.这样,看似每个线程都在并发访问同一个资源(线程局