• 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取到值。

  • 相关阅读:
    ES5 ES6 作用域声明部分
    js 内建函数reduce
    $apply的使用与否
    得分-星星
    CSS3中translate、transform和translation的区别和联系
    vue 学习笔记
    -webkit-line-clamp 多行文字溢出...
    八位二进制数为什么表示范围(-128~~+127)理解
    vs2017_enterprise正式版离线安装包bt下载
    RSA密钥之C#格式与Java格式转换
  • 原文地址:https://www.cnblogs.com/devise/p/9997049.html
Copyright © 2020-2023  润新知