ThreadLocal的使用

 引言

  在高并发的情况下,线程安全是尤其重要的,其中线程安全又分为多个方面,安全发布对象、不可变对象、线程封闭等。其中,线程封闭就是将变量封装到一个线程中, 这样并发的其他线程就无法看到和使用该变量,这样就保证了线程的安全性。而线程封闭的其中一种就是使用ThreadLocal,这在我们实际开发中也是非常有用的。


ThreadLocal介绍

  ThreadLocal主要是通过以Map的形式对数据进行存储,其中 key为当前线程,而value值是我们传入的参数;使用ThreadLocal可以让我们的代码更简洁,并且在想使用我们存储数据的任何地方都可以拿到。最重要的是它是线程封闭的,也就是线程安全的,适用于在多线程场合。

ThreadLocal部分源码

    ThreadLocal最常用的3个方法,一、设置值,二、取出值,三、清空值(请及时清空,以免发生内存溢出的情况)
设置值
  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

取出值

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

清空值

    public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

使用场景

通常情况下,都是结合过滤器和拦截器进行使用,在过滤器时存值,然后在接口执行结束时清空值

实例


以springboot中使用ThreadLocal为例

定义ThreadLocal操作类

 1 package com.me.concurrency.threadlocal;
 2 
 3 public class RequestHolder {
 4 
 5     private static ThreadLocal<Long> threadId = new ThreadLocal<>();
 6 
 7     public static void add(Long id) {
 8         threadId.set(id);
 9     }
10 
11     public static void remove() {
12         threadId.remove();
13     }
14 
15     public static Long get() {
16         return threadId.get();
17     }
18 
19 
20 }


定义过滤器

 1 package com.me.concurrency.threadlocal;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 import org.springframework.stereotype.Component;
 5 
 6 import javax.servlet.*;
 7 import javax.servlet.http.HttpServletRequest;
 8 import java.io.IOException;
 9 
10 @Slf4j
11 public class HttpFilter implements Filter {
12     @Override
13     public void init(FilterConfig filterConfig) throws ServletException {
14 
15     }
16 
17     @Override
18     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
19         HttpServletRequest request = (HttpServletRequest) servletRequest;
20         log.info("currentThreadId --{}", Thread.currentThread().getId());
21         log.info("the request url is:{}", request.getRequestURL());
22         RequestHolder.add(Thread.currentThread().getId());
23         filterChain.doFilter(servletRequest, servletResponse);
24     }
25 
26     @Override
27     public void destroy() {
28 
29     }
30 }

定义拦截器

 1 package com.me.concurrency.threadlocal;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 import org.springframework.web.servlet.ModelAndView;
 5 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 6 
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 @Slf4j
11 public class CustomInterceptor extends HandlerInterceptorAdapter {
12 
13     @Override
14     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
15         return true;
16     }
17 
18     // 正常结束
19     @Override
20     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
21         super.postHandle(request, response, handler, modelAndView);
22     }
23 
24     // 正常与异常结束
25     @Override
26     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
27         log.info("interceptor compeleted..");
28         RequestHolder.remove();
29     }
30 
31 
32 }

springboot中过滤器和拦截器的配置

 1 package com.me.concurrency.config;
 2 
 3 import com.me.concurrency.threadlocal.CustomInterceptor;
 4 import com.me.concurrency.threadlocal.HttpFilter;
 5 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
10 
11 @Configuration
12 public class WebMvcConfig extends WebMvcConfigurerAdapter {
13 
14 
15     @Bean
16     public FilterRegistrationBean httpFilter() {
17         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
18         filterRegistrationBean.setFilter(new HttpFilter());
19         filterRegistrationBean.addUrlPatterns("/threadLocal/*"); // 过滤的请求
20         return filterRegistrationBean;
21     }
22 
23     @Override
24     public void addInterceptors(InterceptorRegistry registry) {
25         registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/threadLocal/**"); //拦截的请求
26     }
27 }

测试接口

@RequestMapping("/threadLocal/getId")
    @ResponseBody
    public Long threadLocal(){
        return RequestHolder.get();
    }

执行结果

写在最后

ThreadLocal在项目中的使用非常常见,它能将我们需要存储的值以key为当前线程,value为实际存储值的形式存储在map的数据结构中,并且对存储的值进行了线程封闭,其他线程是不能看到和使用的,所以是线程安全的,适用于多线程,高并发的场景下;而且,使用ThreadLocal可以简化我们的代码,我们不需要将存储值一层一层的传递下去,而是在任何地方直接通过ThreadLocal取到值。

原文地址:https://www.cnblogs.com/devise/p/9997049.html