Spring boot

Spring如何处理线程并发:ThreadLocal

2020-12-02  本文已影响0人  三也视界

SpringMVC是单例的,高并发情况下,如何保证性能的?

按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但Spring的DAO模板类并未采用线程同步机制,因为线程同步限制了并发访问,会带来很大的性能损失。

此外,通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现难度。那模板类究竟仰丈何种魔法神功,可以在无需同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!

spring单例模式下用它来切换不同线程之间的参数。用ThreadLocal是为了保证线程安全,实际上ThreadLoacal的key就是当前线程的Thread实例。单例模式下,spring把每个线程可能存在线程安全问题的参数值放进了ThreadLocal。这样虽然是一个实例在操作,但是不同线程下的数据互相之间都是隔离的,因为运行时创建和销毁的bean大大减少了,所以大多数场景下这种方式对内存资源的消耗较少,而且并发越高优势越明显。

总的来说就是,单利模式因为大大节省了实例的创建和销毁,有利于提高性能,而ThreadLocal用来保证线程安全性。

另外补充说一句,单例模式是spring推荐的配置,它在高并发下能极大的节省资源,提高服务抗压能力。spring IoC的bean管理器是“绝对的线程安全”。

ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。

ThreadLocal是什么

早在JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

线程局部变量并不是Java的新发明,很多语言(如IBM IBM XLFORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。

所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

void set(Object value)

设置当前线程的线程局部变量的值。

public Object get()

该方法返回当前线程所对应的线程局部变量。

public void remove()

将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

protected Object initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是voidset(T value)、T get()以及T initialValue()。

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

SpringBoot在Controller类中自动注入HttpServletRequest是否线程安全解析

1、首先大家先看一段代码
@RestController
@RequestMapping("/myTest")
public class MyTestController {

    @Autowired
    private HttpServletRequest request;

    @GetMapping("/add" )
    @ResponseBody
    public String add() {
        String name=request.getParameter("name");
        return name;
    }
}

2、相信大家都知道spring中类的作用域默认为单例,既然是单例那么上述代码一定不是线程安全的,但是经过测试却发现 上述代码是线程安全(测试过程这里不写了),我们写法没有任何问题。这是为什么呢?接下来我们一步一步通过源码查找这个问题。
3、通过上图我们会发现request被注入的对象是一个动态代理生成的类,并且objectFactory是WebApplicationContextUtils中的内部类RequestObjectFactory。如下图
image.png

4、那么它是如何被调用的呢?
4.1、spring在初始化bean对象的时候会调用WebApplicationContextUtils----->registerWebApplicationScopes();该方法部分代码如下:
//注册解析依赖各种信息
beanFactory.registerResolvableDependency(ServletRequest.class, new WebApplicationContextUtils.RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new WebApplicationContextUtils.ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new WebApplicationContextUtils.SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebApplicationContextUtils.WebRequestObjectFactory());

这段代码是将对应的类以及工厂类缓存好,方便后续使用。最终放在类DefaultListableBeanFactory的resolvableDependencies中。代码如下
@Override
    public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
        Assert.notNull(dependencyType, "Dependency type must not be null");
        if (autowiredValue != null) {
            if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
                throw new IllegalArgumentException("Value [" + autowiredValue +
                        "] does not implement specified dependency type [" + dependencyType.getName() + "]");
            }
            //这里将解析的依赖放入到resolvableDependencies中
            this.resolvableDependencies.put(dependencyType, autowiredValue);
        }
    }

4.2最终spring在装填Bean的属性时,会调用DefaultListableBeanFactory---->findAutowireCandidates()具体代码如下:
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
        //循环遍历存储在Map集合resolvableDependencies中的类
        for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
            Class<?> autowiringType = classObjectEntry.getKey();
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = classObjectEntry.getValue();
                //这里会动态代理生成一个对象 当遍历到javax.servlet.ServletRequest.class时
                //autowiringValue=new WebApplicationContextUtils.RequestObjectFactory()
                //requiredType=javax.servlet.ServletRequest
                //autowiringValue = ServletRequest.class
                AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }

4.3 进如AutowireUtils.resolveAutowiringValue代码如下
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
        if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
            //autowiringValue=new WebApplicationContextUtils.RequestObjectFactory(){这里时我们上一步传入的}
            ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
            if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
            //关键代码,在这里动态代理创建对象
                autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
                        new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
            }
            else {
                return factory.getObject();
            }
        }
        return autowiringValue;
    }

4.4、进入ObjectFactoryDelegatingInvocationHandler,代码如下
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

        private final ObjectFactory<?> objectFactory;

        public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
            this.objectFactory = objectFactory;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if (methodName.equals("equals")) {
                // Only consider equal when proxies are identical.
                return (proxy == args[0]);
            }
            else if (methodName.equals("hashCode")) {
                // Use hashCode of proxy.
                return System.identityHashCode(proxy);
            }
            else if (methodName.equals("toString")) {
                return this.objectFactory.toString();
            }
            try {
            /**
            * 动态代理生成类执行方法时,都会走下面这行代码,
            * this.objectFactory.getObject() 就是执行的真正的实例对象,这里的this.objectFactory就是上一步我们传入的new WebApplicationContextUtils.RequestObjectFactory()
            */
                return method.invoke(this.objectFactory.getObject(), args);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }

4.5、通过上面代码我们会发现,代码最终会调用new WebApplicationContextUtils.RequestObjectFactory().getObject(),其代码如下:
public ServletRequest getObject() {
            return WebApplicationContextUtils.currentRequestAttributes().getRequest();
        }

WebApplicationContextUtils.currentRequestAttributes() 最终会从RequestContextHolder的requestAttributesHolder获取RequestAttributes。而requestAttributesHolder定义如下:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");

4.6、这时我们会有一个疑问,那requestAttributesHolder中的值具体是从哪里来的呢?当我们从前端发起一个请求后,代码会进入FrameworkServlet----->processRequest()---->initContextHolders(),init代码如下:
private void initContextHolders(HttpServletRequest request,
            @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

        if (localeContext != null) {
            LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
        }
        //将当前请求的RequestAttributes放入到RequestContextHolder
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        }
    }

至此,我们也就清晰了。(我们这里用开头代码举例),整体流程就是当服务启动时,spring装填MyTestController类的属性时,会将一个动态生成的类注入request中。当前端发起一个请求后/myTest/add?name=“lisi”,spring会将当前的requestAttributes放入到RequestContextHolder---->requestAttributesHolder中。当代码执行到String name=request.getParameter(“name”);时,实际调用的是:WebApplicationContextUtils.currentRequestAttributes().getRequest().getParameter(“name”);而WebApplicationContextUtils.currentRequestAttributes()获取到的requestAttributes就是我们发起请求时放入到RequestContextHolder中的requestAttributes。而且requestAttributesHolder是ThreadLocal的,所以是线程安全的。
上一篇下一篇

猜你喜欢

热点阅读