通过将不可以被修改的对象设计成不可变对象,实现在多线程下线程安全,躲避开并发问题
将一个对象封装到一个线程里,只有一个线程可以看到访问, 从而安全,以下是实现线程封闭的四种方法:
通过ThreadLocal + filter 实现一个类在接口处理前已经取好,随用随取,而不是把一个用户信息从request中取出再从controller一直传下去。绑定到线程上的信息,
package com.mmall.concurrency.threadlocal;
public class RequestHolder {
public static final ThreadLocal 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();
}
}
package com.mmall.concurrency;
import com.mmall.concurrency.threadlocal.RequestHolder;
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;
// 打印请求信息
log.info("do filter:{}, {}", Thread.currentThread().getId(), request.getServletPath());
// request.getSession().getAttribute(); 就可以获取信息
RequestHolder.add(Thread.currentThread().getId());
// 让拦截器执行完, 让请求继续被处理。
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
在启动类里配置,配置完后,add() 方法已经可以用了,下面写remove方法,
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) {
//super.addInterceptors(registry);
registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
}
}
package com.mmall.concurrency;
import com.mmall.concurrency.threadlocal.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.ModelAndView;
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;
}
// 正常请求之后
// public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { }
// 无论正常还是异常执行结束,都执行这个方法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
RequestHolder.remove();
log.info("执行完毕");
return;
}
// public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
继承类并且复写, 然后调用方法
package com.mmall.concurrency.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();
}
}
当一个请求进来之后,filter将一个Thread.currentThread().getId(),ID存放在threadlocal里面,
当接口实际处理是,调用return RequestHolder.getId(); 取出来,
当接口处理完毕后,调用intercept来移除。
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
RequestHolder.remove();
log.info("执行完毕");
return;
}
看看threadlocal里面我们做了哪些事情。
*1. 首先定义一个threadLocal,里面有添加,获取,移除的方法,
(public static final ThreadLocal requestHolder = new ThreadLocal<>()?
*2. 其次定义一个filter,在执行体里调用add方法,将数据存放在threadLocal中
public class HttpFilter implements Filter
*3. 最后定义一个intercept拦截器,在执行后删除解除一些资源,
class HttpInterceptor extends HandlerInterceptorAdapter
*当controller 执行时,直接取
实际在服务器应用程序中,线程从连接池获取了一个connection对象,使用完后返回给连接池,
由于大多数请求是通过单线程同步方式处理,并且在connection对象返回去,连接池不会将其分配给其他线程,处理隐含的将connection封闭在线程里 – 安全-- 线程封闭