线程封闭
当访问共享Data时,通常是要UsageSynchronization。如果要避免UsageSynchronization,就是不提供共享Data。如果仅在单Thread中访问Data,就不需要Synchronization,这种技术就叫做ThreadConfinement,它是ImplementationThreadSafety最简单的方式之一。当某个对象Confinement在一个Thread当中时将自动ImplementationThreadSafety性,即使被Confinement的对象本身它并不是Safety的,ImplementationThread主要有三种方式。
ImplementationThreadConfinement的三种方式:
1、Ad-hocThreadConfinement:程序控制Implementation,最糟糕,忽略
2、堆栈Confinement:局部变量,无ConcurrencyProblem:
栈Confinement简单理解就是通过局部变量来ImplementationThreadConfinement,多个Thread访问对象的同一个Method,Method内部的局部变量会拷贝到每个Thread的Thread栈当中,只有当前Thread才能访问到,互不干扰。所以局部变量是不被多个Thread所共享的。尽量少用全局变量(不是全局常量)。
3、ThreadLocalThreadConfinement:特别好的ConfinementMethod。
维护ThreadConfinement一种更规范的Method就是UsageThreadLocal。ThreadLocal底层是一个map,可以就是Thread名字,value就是我们封装的对象。ThreadLocal提供get、setMethod为每个Usage该变量的Thread都存有一份独立的副本,因此get总是Return的是当前Thread在调用set时Settings的值。
ThreadLocal一般用于防止对可变的单Example变量或者全局变量进行共享。
ThreadLocalUsage:Code Example
1、自定义ThreadLoca工具
package com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock;
/**
* @description: 使用ThrodLock的线程封闭,实现在一个线程种数据共享
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 22:56
* @version: 1.0
*/
public class MyThroldLock {
private final static ThreadLocal<Long> requestholder = new ThreadLocal<>();
//用ThreadLock提供的set方法,向ThreadLock中放入共享数据id
//一般在实际接口处理前调用
public static void add(Long id) {
requestholder.set(id);
}
//用ThreadLock提供的get方法获取ThreadLock中共享的数据
public static Long get() {
return requestholder.get();
}
//用ThreadLock提供的remove方法移除共享数据,不然容易导致内存溢出
//一般在接口真正处理完后调用
public static void remove() {
requestholder.remove();
}
}
2、自定义过滤器Filter
package com.yanxizhu.demo.concurrency.DemoThrodLocal.filter;
import com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock.MyThroldLock;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @description: 自定一自己的filter,实现filter接口,注意是servlet中的
* 说明:
* Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,
* 主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是,
* 只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,
* 此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;
* 同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。
* 它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 23:02
* @version: 1.0
*/
@Slf4j
public class HttpFilter implements Filter {
//初始化的
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//一般需要强转成HttpServletRequest、HttpServletResponse,这样才能直接获取用户浏览器访问时携带信息
//比如获取用户session中的用户信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//一般登录会将用户信息写入session,这里只是演示一下
// String userName = (String) request.getSession().getAttribute("userName");
log.info("do filter,线程id {},请求地址{}", Thread.currentThread().getId(), request.getServletPath());
//通过向ThreadLock添加共享值
MyThroldLock.add(Thread.currentThread().getId());
// 所有处理完后,记得执行下面,不然不会往下执行
filterChain.doFilter(servletRequest, servletResponse);
}
//销毁时的
public void destroy() {
}
}
注册自定义过滤器HttpFilter
package com.yanxizhu.demo.concurrency.DemoThrodLocal.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description: 将自定义的filter添加到spring拦截器中
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 23:37
* @version: 1.0
*/
@Configuration
public class MyFilterConfig {
@Bean
public FilterRegistrationBean httpFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new HttpFilter());
filterRegistrationBean.addUrlPatterns("/demo/*");
return filterRegistrationBean;
}
}
3、自定义拦截器HandlerInterceptor
package com.yanxizhu.demo.concurrency.DemoThrodLocal.interceptor;
import com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock.MyThroldLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @description: 自定义HandlerInerceptor
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 22:28
* @version: 1.0
*/
@Slf4j
@Component
public class MyHandlerInterceptor implements HandlerInterceptor {
//接口处理之前
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("进入controller之前的preHanler方法。。。。");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("这个是干嘛的呢。。。");
}
//接口处理之后
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("之后的方法。。。。afterCompletion,MyThroldLock.remove()");
MyThroldLock.remove();
return;
}
}
注册自定义拦截器MyHandlerInterceptor
package com.yanxizhu.demo.concurrency.DemoThrodLocal.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @description: 自定义WebMVCConfig
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 22:31
* @version: 1.0
*/
@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
@Autowired
MyHandlerInterceptor myHandlerInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myHandlerInterceptor).addPathPatterns("/**");
}
}
4、编写Controller测试ThreadLock的使用
package com.yanxizhu.demo.concurrency.DemoThrodLocal.controller;
import com.yanxizhu.demo.concurrency.DemoThrodLocal.ThreadLock.MyThroldLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @description: HandlerInterceptor 拦截器测试
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/22 22:25
* @version: 1.0
*/
@RestController
@RequestMapping("/demo")
@Slf4j
public class ThreadLockController {
@GetMapping("/myHandlerInterceptor")
public String myHandlerInterceptorTest() {
log.info("进入Controoler方法种。。。。。。");
return "My HandlerInterceptor....,共享ThreadLock的变量为:"+MyThroldLock.get();
}
}
Test请求地址
http://127.0.0.1:8080/demo/myHandlerInterceptor
页面输出结果:
My HandlerInterceptor....,共享ThreadLock的变量为:46
IDEA控制台输出结果:
do filter,线程id 50,请求地址/demo/myHandlerInterceptor
进入controller之前的preHanler方法。。。。
进入Controoler方法种。。。。。。
这个是干嘛的呢。。。
之后的方法。。。。afterCompletion
注意点
注意过滤器Filter和拦截器HandlerInterceptor的区别及Usage方式。
可参考地址:https://blog.csdn.net/yb546822612/article/details/102950040