ThreadLocal小试牛刀

  • 2019 年 11 月 2 日
  • 筆記

ThreadLocal中保存的数据只能被当前线程私有,不被其它线程可见

证明

声明一个全局的变量threadLocal,初始值为1,通过3个线程对其进行访问修改设置,理论上threadLocal的最终值应该是6,然而我们的输出结果是3,说明了threadLocal中存放的数据是各自线程私有的

package com.mmall.concurrency.example.threadLocal;    public class UseThreadLocal {      static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {          @Override          protected Integer initialValue() {              return 1;          }      };          //运行3个线程      public void startThreadArray() {          Thread[] thread = new Thread[3];          for (int i = 0; i < thread.length; i++) {              thread[i] = new Thread(new MyThread(i));          }            for (int i = 0; i < thread.length; i++) {              thread[i].start();          }      }        private class MyThread implements Runnable {          int id;            public MyThread(int i) {              id = i;          }            @Override          public void run() {              System.out.println(Thread.currentThread().getName()+":start");              Integer v = threadLocal.get();              v=v+id;              threadLocal.set(v);              System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());          }      }        public static void main(String[] args) {          UseThreadLocal useThreadLocal = new UseThreadLocal();          useThreadLocal.startThreadArray();      }  }  

结果

Thread-0:start  Thread-2:start  Thread-1:start  Thread-0:1  Thread-2:3  Thread-1:2

小应用

ThreadLocal结合过滤器和拦截器进行搭配使用,通过在过滤器HttpFilter设置ThreadLocal中的值,通过拦截器HttpInterceptor移除拦截器中的值

编写`ThreadLocal类,包含设置、获取、移除操作

package com.mmall.concurrency.example.threadLocal;    public class RequestHolder {        private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();        public static void add(Long id) {          requestHolder.set(id);      }        public static Long getId() {          return requestHolder.get();      }        public static void remove() {          requestHolder.remove();      }  }  

编写过滤器HttpFilter类,通过在doFilter方法中对ThreadLocal进行存数据

package com.mmall.concurrency;    import com.mmall.concurrency.example.threadLocal.RequestHolder;  import lombok.extern.slf4j.Slf4j;    import javax.servlet.Filter;  import javax.servlet.FilterChain;  import javax.servlet.FilterConfig;  import javax.servlet.ServletException;  import javax.servlet.ServletRequest;  import javax.servlet.ServletResponse;  import javax.servlet.http.HttpServletRequest;  import java.io.IOException;    @Slf4j  public class HttpFilter implements Filter {        @Override      public void init(FilterConfig filterConfig) throws ServletException {        }        @Override      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {          HttpServletRequest request = (HttpServletRequest) servletRequest;          log.info("do filter, {}, {}", Thread.currentThread().getId(), request.getServletPath());          RequestHolder.add(Thread.currentThread().getId());          filterChain.doFilter(servletRequest, servletResponse);      }        @Override      public void destroy() {        }  }  

编写ThreadLocalController类,在业务中可以获取到在过滤器HttpFilter中对ThreadLocal中存放的数据

package com.mmall.concurrency.example.threadLocal;    import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.ResponseBody;    @Controller  @RequestMapping("/threadLocal")  public class ThreadLocalController {        @RequestMapping("/test")      @ResponseBody      public Long test() {          return RequestHolder.getId();      }  }  

编写拦截器HttpInterceptor类,在完成业务逻辑处理后,在拦截器类HttpInterceptorafterCompletion方法中移除我们在过滤器HttpFilter中对ThreadLocal设置的值

package com.mmall.concurrency;    import com.mmall.concurrency.example.threadLocal.RequestHolder;  import lombok.extern.slf4j.Slf4j;  import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;    import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;    @Slf4j  public class HttpInterceptor extends HandlerInterceptorAdapter {        @Override      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {          log.info("preHandle");          return true;      }        @Override      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {          RequestHolder.remove();          log.info("afterCompletion");          return;      }  }  

编写springboot的启动类ConcurrencyApplication,实例化了FilterRegistrationBean

package com.mmall.concurrency;    import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;  import org.springframework.boot.web.servlet.FilterRegistrationBean;  import org.springframework.context.annotation.Bean;  import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;    @SpringBootApplication  public class ConcurrencyApplication extends WebMvcConfigurerAdapter{        public static void main(String[] args) {          SpringApplication.run(ConcurrencyApplication.class, args);      }        @Bean      public FilterRegistrationBean httpFilter() {          FilterRegistrationBean registrationBean = new FilterRegistrationBean();          registrationBean.setFilter(new HttpFilter());          registrationBean.addUrlPatterns("/threadLocal/*");          return registrationBean;      }        @Override      public void addInterceptors(InterceptorRegistry registry) {          registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");      }  }  

启动springboot启动类,访问http://localhost:8080/threadLocal/test,控制台输出

1571820790890

本文由博客一文多发平台 OpenWrite 发布!