Why are Spring beans commonly used thread-safe?

The default scope for creating beans in Spring is singleton, that is, objects are singletons, and when we use these beans, such as services, DAOs, and Controllers, we use them mostly in the form of utility classes. The methods of these beans are only called, and the bean properties and state are not changed. Therefore, there is no multi-threaded race, that is, thread safety.

How do we keep thread-safe if we want to change the properties of these beans?

By setting scope to Prototype

When scope is set to prototype, the object is reconstructed each time it is used, so its properties (member variables) are thread-specific and thread-safe. However, the creation and destruction of so many objects can consume a lot of memory and system resources, and the following threadLocal is recommended.

By using ThreadLocal

When the properties of a synchronous object do not need to be shared with other threads, it only needs to be used when its own thread is closed and not modified by other threads

Threadloca principle

Reference: Java multithreading – ThreadLocal

Demo:

The variables are shared in each request through the ThreadLocal class, and the shared variables are verified by the filter and interceptor. Print the value of threadLocal in the filter. Set the value of threadLocal before the interceptor and print it. Delete the value of ThreadLocal after the interceptor to avoid memory leaks. (PS: In order to explain the convenience, so send all the code to a class file, implementation of use needs to separate)

import lombok.extern.slf4j.Slf4j;
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.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * \* Created with IntelliJ IDEA.
 * \* @author: Date: 2019-06-11 * \* Time: 12:05 * \* Description: * \* /
@Slf4j
@SpringBootApplication
public class JavaStudyApplication implements WebMvcConfigurer {
    public static void main(String[] args) {
        SpringApplication.run(JavaStudyApplication.class, args);
    }

    public static final ThreadLocal threadLocal = new ThreadLocal<>();


    @RestController("thread-local")
    class ThreadLocalController {
        @PostMapping
        public String threadLocalSet(a) {
            threadLocal.set("controller set");
            return "Setup successful!";
        }

        @GetMapping
        public String threadLocalGet(a) {
            return "ThreadLocal obtained: ="+ threadLocal.get(); }}class HttpFilter implements Filter {
        @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.get()); filterChain.doFilter(request, servletResponse); }}class HttpInterceptor extends HandlerInterceptorAdapter {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            threadLocal.set("interceptor set");
            log.info("pre handle , threadLocal:" + threadLocal.get());
            return true;
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            log.info("after completion, threadLocal:"+ threadLocal.get()); threadLocal.remove(); }}@Bean
    public FilterRegistrationBean httpFilter(a) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new HttpFilter());
        registrationBean.addUrlPatterns("/thread-local/*");
        return registrationBean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/ * *"); }}Copy the code

results

If the threadlocal filter is not set, the result is null. If the threadlocal filter is set before the interceptor, the threadlocal set is set. It is also set in the controller and returns the same value as the interceptor set.

Request:

Log:

If POST /thread-local is requested, the result is similar to the following:

Request:

Log:

Through the lock

Used when properties of synchronized objects are shared across multiple threads, see Java multithreaded locking mechanism