Spring如何处理线程并发:ThreadLocal
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.png4、那么它是如何被调用的呢?
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);
}
}