ThreadLocal的深入理解及应用

Easter79
• 阅读 703

是什么

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread , 它类似(Map),用来存储当前运行线程及对应的变量。

在WEB应用中每次Http请求,都相当于从线程池取一个空闲线程对请求的方法作处理。此时当前线程的所有方法中Thread.currentThread()都是一样,然后我们可以把它假想为Map的key,value可以存一个在整个请求中都用到的变量。

好处

1:避免了传参

例如在dao层 **userDao**下有_save(User user)_,_update(Department department_)两种方法,

service中**accessionService**的_accession_方法

boolean accssion(User user, Department department, Connection conn) {

conn.begin();

save(user);

update(department);

conn.commint();

}

为了保证数据一致性,必须在service层添加事务,那么每有一个新的连接(请求),必须要传一个conn参数,做法不好,污染api。

2:解决了高并发中对资源的争夺(牺牲空间换取时间与synchronize相反)。

ThreadLocal源码如下

    //一般我们用ThreadLocal,通过调用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(); //初始化ThreadLocalMap
    }

    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;
    }

    //ThreadLocalMap其实是一个entry(静态内部类)继承了WeakReference,是一个弱关联,并非数组
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }

    //改变当前线程绑定值
    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);
    }

    //map.set()具体实现,根据当前线程key定位到entry[i],再改变entry[i]对应的值为value
    private void set(ThreadLocal<?> key, Object value) {

        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();
    }

应用

在spring切面方法中,我们没有办法直接从ProceedingJoinPoint中获取request和response对象,但是我们要对request的传递参数处理,同时也要用response添加cookie,实现重定向等功能。

//过滤器,每次请求过来都经过这个过滤器处理
public class GetContent implements Filter {
    @Override  
    public void destroy() {  
        // TODO Auto-generated method stub  
    }  
    @Override  
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
                         FilterChain arg2) throws IOException, ServletException {
        SysContent.setRequest((HttpServletRequest) arg0);
        SysContent.setResponse((HttpServletResponse) arg1);
        arg2.doFilter(arg0, arg1);  
    }  
    @Override  
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub  
    }  
}  

//把response、request与当前线程绑定,具体实现
public class SysContent {
     private static ThreadLocal<HttpServletRequest> requestLocal= new   ThreadLocal<HttpServletRequest>();
     private static ThreadLocal<HttpServletResponse> responseLocal= new ThreadLocal<HttpServletResponse>();
       
    public static HttpServletRequest getRequest() {  
        return (HttpServletRequest)requestLocal.get();  
    }  
    public static void setRequest(HttpServletRequest request) {  
        requestLocal.set(request);  
    }  
    public static HttpServletResponse getResponse() {  
        return (HttpServletResponse)responseLocal.get();  
    }  
    public static void setResponse(HttpServletResponse response) {  
        responseLocal.set(response);  
    }  
    public static HttpSession getSession() {
        return (HttpSession)((HttpServletRequest)requestLocal.get()).getSession();  
    }  
}

@Component
@Aspect
public class ActionShareAspect {
    private static final Logger logger = LoggerFactory.getLogger(ActionLogAspect.class);

    //配置切入点
    @Pointcut("execution(public * com.xx.xx.*.*(..))") // 配置切入点
    public void pointcut(){}

    @Around("pointcut()")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        //request、response获取
        HttpServletRequest request = SysContent.getRequest();
        HttpServletResponse response = SysContent.getResponse();

        //执行目标方法
        String methodName = pjd.getSignature().getName();
        Object result = null;
        try {
           //...略
           //返回通知
           result = pjd.proceed();
           return result;
        } catch (Exception e) {
            //...
        }
    }

回顾来思考最初的问题:关于spring的事务。我们可以把每次请求从数据连接池获取的connection绑定到ThreadLocal上,那么就解决了service中事务的问题。当然这里要对service进行切面编程,使用代理,下次博客再细说。

点赞
收藏
评论区
推荐文章
Easter79 Easter79
3年前
spring中,在Java任何位置获取request对象
看RequestContextListener和RequestContextHolder源代码不难看出,是用过ThreadLocal实现的。ThreadLocal是什么呢?看词义,它并非是一个线程,它不是一个Thread,而是一个线程局部变量,其实功能非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而
Wesley13 Wesley13
3年前
java ThreadLocal
ThreadLocal是什么定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)场景:多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)ThreadLocal基本API 构
ThreadLocal源码解析及实战应用
ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。ThreadLocal在设计之初就是为解决并发问题而提供一种方案,每个线程维护一份自己的数据,达到线程隔离的效果。
Wesley13 Wesley13
3年前
Java ThreadLocal的内存泄漏问题
ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现。常见的ThreadLocal用法有:\存储单个线程上下文信息。比如存储id等;\使变量线程安全。变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;\减少参数传递。比如做一个trace工具,能够输出工程从开始到结
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal原理解析
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
3年前
Java并发编程的艺术笔记(四)——ThreadLocal的使用
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
3年前
JAVA基础系列:ThreadLocal
1. 思路1.什么是ThreadLocal?ThreadLocal类顾名思义可以理解为线程本地变量。也就是说如果定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写是线程隔离,互相之间不会影响的。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。2.它大致的实现
Easter79 Easter79
3年前
ThreadLocal 详解
概念ThreadLocal用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。也就是说ThreadLocal可以为每个线程创建一个【单独的变量副本】,相当于线程的privatestatic类型变量。使用示例publicclassThreadLocalTest{
Easter79 Easter79
3年前
ThreadLocal设计模式
ThreadLocal设计模式使用的也很频繁,会经常在各大框架找到它们的踪影,如struts2以及最近正在看的SpringAOP等。ThreadLocal设计模式也有很多误解,我的理解是(1)ThreadLocal所操作的数据是线程间不共享的。它不是用来解决多个线程竞争同一资源的多线程问题。(2)ThreadLocal所操作的数据主要
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k