三 案例
目录结构:
HttpFilter.java
package com.lyy.threadlocal.config; import lombok.extern.slf4j.Slf4j; import javax.servlet.*; 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; // request.getSession().getAttribute("user"); System.out.println("do filter:"+Thread.currentThread().getId()+":"+request.getServletPath()); RequestHolder.add(Thread.currentThread().getId()); //让这个请求完,,同时做下一步处理 filterChain.doFilter(servletRequest,servletResponse); } //不再使用的时候做的事情 @Override public void destroy() { } }
HttpInterceptor.java
package com.lyy.threadlocal.config; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HttpInterceptor extends HandlerInterceptorAdapter { //接口处理之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle:"); return true; } //接口处理之后 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { RequestHolder.remove(); System.out.println("afterCompletion"); return; } }
RequestHolder.java
package com.lyy.threadlocal.config; public class RequestHolder { private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();// //提供方法传递数据 public static void add(Long id){ requestHolder.set(id); } public static Long getId(){ //传入了当前线程的ID,到底层Map里面去取 return requestHolder.get(); } //移除变量信息,否则会造成逸出,导致内容永远不会释放掉 public static void remove(){ requestHolder.remove(); } }
ThreadLocalController.java
package com.lyy.threadlocal.controller; import com.lyy.threadlocal.config.RequestHolder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/thredLocal") public class ThreadLocalController { @RequestMapping("test") @ResponseBody public Long test(){ return RequestHolder.getId(); } }
ThreadlocalDemoApplication.java
package com.lyy.threadlocal; import com.lyy.threadlocal.config.HttpFilter; import com.lyy.threadlocal.config.HttpInterceptor; 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 ThreadlocalDemoApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(ThreadlocalDemoApplication.class, args); } @Bean public FilterRegistrationBean httpFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new HttpFilter()); registrationBean.addUrlPatterns("/thredLocal/*"); return registrationBean; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**"); } }
四 总结
1、ThreadLocal是通过每个线程单独一份存储空间,每个ThreadLocal只能保存一个变量副本。
2、相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值,很好的实现了线程封闭。
3、每次使用完ThreadLocal,都调用它的remove()方法,清除数据,避免内存泄漏的风险
4、通过上面的源码分析,我们也可以看到大神在写代码的时候会考虑到整体实现的方方面面,一些逻辑上的处理是真严谨的,我们在看源代码的时候不能只是做了解,也要看到别人实现功能后面的目的。
源码地址:https://github.com/839022478/other/tree/master/threadlocal_demo