并发编程与高并发解决方案学习(线程封闭-ThreadLocal)

一、概念

线程封闭就是把对象封装到一个线程里,只有一个线程能看到这个对象,那么这个对象不是线程安全的,也不会出现线程安全问题,因为只能在一个线程里面访问。

二、线程封闭的三种方式:

Ad-hoc线程封闭:程序控制实现,最糟糕,忽略

堆栈封闭:局部变量,无并发问题

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


 三、有的人会有疑问:之前没有学过并发,为什么我的程序大部分执行也都是没有问题的呢?

    其实就是因为大部分需求,写接口的时候都是方法内部定义局部变量来完成各种操作的(堆栈封闭)

正常情况下,每一个请求都是对应服务器里面一个线程运行,我们希望的是线程间隔离,那么线程在被后端服务器实际处理的时候通过Filter可以直接先取出来当前的用户,然后把数据放到ThreadLocal里面,当这个线程被Service处理的时候,可能就需要取出当前用户,这个时候我们就可以在ThreadLocal里面取值了。因为我们登陆的用户通常都是从request取出来,因此可能带着request或者从request取出的用户信息,从Controller开始不停的往下传递,甚至可能传递到某些Utils类里面去,这样看起来不好,如果我们使用ThreadLocal+Filter,就可以在接口处理前取出相关信息,在接口实际处理的时候,什么时候需要,就从ThreadLocal里面取相关信息。

ThreadLocal例子

public class RequestHolder {

    private final static ThreadLocal requestHolder = new ThreadLocal<>();

    /**
     * 放数据
     * @param id
     */
    public static void add(Long id) {
        requestHolder.set(id);
    }

    /**
     *  移除数据
     * @return
     */
    public static Long getId() {
        return requestHolder.get();
    }

    /**
     * 获取数据
     */
    public static void remove() {
        requestHolder.remove();
    }
}
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());
        //向ThreadLocal放数据
        RequestHolder.add(Thread.currentThread().getId());
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

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

/**
 * 数据处理完了,在Interceptor将数据移除出去,避免内存泄漏
 */
@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;
    }


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

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 {
    /**
     * 接口里面可以实时获取在ThreadLocal里面存放过的数据
     * @return
     */
    @RequestMapping("/test")
    @ResponseBody
    public Long test() {
        return RequestHolder.getId();
    }
}




你可能感兴趣的:(并发编程,Java并发编程)