线程封闭
当访问共享数据时,通常是要使用同步。如果要避免使用同步,就是不提供共享数据。如果仅在单线程中访问数据,就不需要同步,这种技术就叫做线程封闭,它是实现线程安全最简单的方式之一。当某个对象封闭在一个线程当中时将自动实现线程安全性,即使被封闭的对象本身它并不是安全的,实现线程主要有三种方式。
实现线程封闭的三种方式:
1、Ad-hoc线程封闭:程序控制实现,最糟糕,忽略
2、堆栈封闭:局部变量,无并发问题:
栈封闭简单理解就是通过局部变量来实现线程封闭,多个线程访问对象的同一个方法,方法内部的局部变量会拷贝到每个线程的线程栈当中,只有当前线程才能访问到,互不干扰。所以局部变量是不被多个线程所共享的。尽量少用全局变量(不是全局常量)。
3、ThreadLocal线程封闭:特别好的封闭方法。
维护线程封闭一种更规范的方法就是使用ThreadLocal。ThreadLocal底层是一个map,可以就是线程名字,value就是我们封装的对象。ThreadLocal提供get、set方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回的是当前线程在调用set时设置的值。
ThreadLocal一般用于防止对可变的单实例变量或者全局变量进行共享。
ThreadLocal使用:代码示例
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();
}
}
测试请求地址
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的区别及使用方式。
可参考地址:https://blog.csdn.net/yb546822612/article/details/102950040
评论 (0)