[6]线程封闭技术

避免并发方法1:不可变对象

通过将不可以被修改的对象设计成不可变对象,实现在多线程下线程安全,躲避开并发问题

避免并发方法2:线程封闭

将一个对象封装到一个线程里,只有一个线程可以看到访问, 从而安全,以下是实现线程封闭的四种方法:
[6]线程封闭技术_第1张图片
通过ThreadLocal + filter 实现一个类在接口处理前已经取好,随用随取,而不是把一个用户信息从request中取出再从controller一直传下去。绑定到线程上的信息,

  • add() , 应该是request过到后端服务器,在请求没有处理前就执行add,去相关信息,用filter。filter首先拦截url,到前台访问URL时,filter将信息写到threadlocal去,-- 在URL处理时候,就可以直接去信息了。
  • remove(), 在接口处理完后调用, – 拦截器intercept,

RequestHolder 保存用户信息

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() {

    }
}

接下来配置filter-让系统指定要过滤,拦截哪些请求

在启动类里配置,配置完后,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 执行时,直接取

在这里主要学会 spring boot中使用过滤器,拦截器,和threadLocal的使用,实现线程封闭技术。

threadLocal使用场景:数据库连接对于jdbc的Connection对象,

实际在服务器应用程序中,线程从连接池获取了一个connection对象,使用完后返回给连接池,
由于大多数请求是通过单线程同步方式处理,并且在connection对象返回去,连接池不会将其分配给其他线程,处理隐含的将connection封闭在线程里 – 安全-- 线程封闭

你可能感兴趣的:(Java并发编程及并发面试点)