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 發佈!