线程封闭

admin
2022-04-23 / 0 评论 / 38 阅读 / 正在检测是否收录...

线程封闭

当访问共享数据时,通常是要使用同步。如果要避免使用同步,就是不提供共享数据。如果仅在单线程中访问数据,就不需要同步,这种技术就叫做线程封闭,它是实现线程安全最简单的方式之一。当某个对象封闭在一个线程当中时将自动实现线程安全性,即使被封闭的对象本身它并不是安全的,实现线程主要有三种方式。

实现线程封闭的三种方式:

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

https://blog.csdn.net/yb546822612/article/details/102950981

3

评论 (0)

取消