java并发---线程封闭-基于SpringBoot的ThreadLocal的实现

 线程封闭一般通过以下三个方法:

    1.Ad-hoc线程封闭:程序控制实现,最糟糕,忽略
    2.堆栈封闭:局部变量,无并发问题

    3.ThreadLocal线程封闭:特别好的封闭方法

方法2是最常用的,变量定义在接口内,本文主要讲解方法三。

本例程是基于SpringBoot框架,并使用两个拦截器,分别为Filter和HandlerInterceptorAdapter 

    第一步,先定义拦截器继承Filter接口,实现ThredLocal.add()方法

    第二步,在Application类实现Filter接口拦截/threadLocal/*的URL

    第三步,封装ThredLocal的方法

    第四步,实现另外个更细粒度的拦截器,HandlerInterceptorAdapter,调用ThredLocal.remove()方法

    第五步,定义调用接口

第一步,先定义拦截器,比如拦截特定的URL

/*
 *Created by William on 2018/4/30 0030
 * 拦截器
 */
@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() {

    }
}

第二步,在Application类实现Filter接口拦截/threadLocal/*的URL

/*
 *Created by William on 2018/4/26 0026
 */
@SpringBootApplication
public class ConcurrencyApplication extends WebMvcConfigurerAdapter {
    public static void main(String[] args) {
        SpringApplication.run(ConcurrencyApplication.class, args);
    }

    /**
     * 添加过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean httpFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new HttpFilter());
        filterRegistrationBean.addUrlPatterns("/threadLocal/*");
        return filterRegistrationBean;
    }

    /**
     * 请求前拦截处理
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
    }
}

第三步,封装ThredLocal的方法

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

第四步,实现另外个更细粒度的拦截器,HandlerInterceptorAdapter

HandlerInterceptorAdapter的介绍:http://www.cnblogs.com/EasonJim/p/7704740.html,相当于一个Filter拦截器,但是这个颗粒度更细,能使用Spring的@Autowired注入,代码如下,对threadLocal进行remove

/*
 *Created by William on 2018/4/30 0030
 */
@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;
    }
}

第五步,定义调用接口

@Controller
@RequestMapping("/threadLocal")
public class ThreadLocalController {
    @RequestMapping("/test")
    @ResponseBody
    public Long test() {
        return RequestHolder.getId();
    }
}

直接从RequestHolder获取之前存储的信息,结果如下:

2018-04-30 19:15:34.573  INFO 6648 --- [nio-8080-exec-8] concurrency.HttpFilter                   : do filter, 46, /threadLocal/test
2018-04-30 19:15:34.574  INFO 6648 --- [nio-8080-exec-8] concurrency.HttpInterceptor              : preHandle
2018-04-30 19:15:34.577  INFO 6648 --- [nio-8080-exec-8] concurrency.HttpInterceptor              : afterCompletion

猜你喜欢

转载自blog.csdn.net/weianluo/article/details/80151220