Spring Bean的线程安全


         Spring 的 bean 作用域(scope)类型

         1、singleton:单例,默认作用域。

         2、prototype:原型,每次创建一个新对象。

         3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。

         4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。

         5、global-session:全局会话,所有会话共享一个实例。

 

         线程安全这个问题,要从单例与原型Bean分别进行说明。

 

         单例singletonBean:所有线程都共享一个单例实例Bean,因此是存在资源的竞争, 如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身

         原型(prototype)Bean:每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

         对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolderTransactionSynchronizationManagerLocaleContextHolder等,使用ThreadLocal的好处 使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。

 

          下面分析下通过TreadLocal解决线程安全的三个类:

RequestContextHolder 代码演示

ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); //获取HttpServletRequest
HttpServletRequest request = requestAttributes.getRequest();

RequestContextHolder 部分源码

public abstract class RequestContextHolder { private static final boolean jsfPresent = ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context"); public RequestContextHolder() { } ------------------------------------省略部分代码----------------------------------- @Nullable public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get(); if (attributes == null) { attributes = (RequestAttributes)inheritableRequestAttributesHolder.get(); } return attributes; } ------------------------------------省略部分代码-----------------------------------

 

LocaleContextHolder  代码演示

Locale locale = LocaleContextHolder.getLocale(); System.out.println(locale.toString());//语言

LocaleContextHolder 部分源码

public final class LocaleContextHolder { private static final ThreadLocal<LocaleContext> localeContextHolder =
            new NamedThreadLocal<>("LocaleContext"); private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
            new NamedInheritableThreadLocal<>("LocaleContext"); // Shared default locale at the framework level
 @Nullable private static Locale defaultLocale; // Shared default time zone at the framework level
 @Nullable private static TimeZone defaultTimeZone; ------------------------------------省略部分代码-----------------------------------
            
public static Locale getLocale() { return getLocale(getLocaleContext()); } ------------------------------------省略部分代码----------------------------------- @Nullable public static LocaleContext getLocaleContext() { LocaleContext localeContext = localeContextHolder.get(); if (localeContext == null) { localeContext = inheritableLocaleContextHolder.get(); } return localeContext; } ------------------------------------省略部分代码-----------------------------------

 

TransactionSynchronizationManager 代码演示

@Transactional public void doInTransaction() { TransactionSynchronizationManager.bindResource("key", "value");//ThreadLocal维护
 TransactionSynchronizationManager.registerSynchronization (new TransactionSynchronizationAdapter() { @Override public void afterCommit() { System.out.println("transaction commit"); } }); }
TransactionSynchronizationManager 部分源码
public abstract class TransactionSynchronizationManager { private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class); private static final ThreadLocal<Map<Object, Object>> resources =
         new NamedThreadLocal<>("Transactional resources"); private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
         new NamedThreadLocal<>("Transaction synchronizations"); private static final ThreadLocal<String> currentTransactionName =
         new NamedThreadLocal<>("Current transaction name"); private static final ThreadLocal<Boolean> currentTransactionReadOnly =
         new NamedThreadLocal<>("Current transaction read-only status"); private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
         new NamedThreadLocal<>("Current transaction isolation level"); private static final ThreadLocal<Boolean> actualTransactionActive =
         new NamedThreadLocal<>("Actual transaction active"); ------------------------------------省略部分代码-----------------------------------
    
public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map<Object, Object> map = resources.get(); // set ThreadLocal Map if none found
    if (map == null) { map = new HashMap<>(); resources.set(map); } Object oldValue = map.put(actualKey, value); // Transparently suppress a ResourceHolder that was marked as void...
    if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { oldValue = null; } if (oldValue != null) { throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) { logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); } } ------------------------------------省略部分代码-----------------------------------
        
    public static void registerSynchronization(TransactionSynchronization synchronization) throws IllegalStateException { Assert.notNull(synchronization, "TransactionSynchronization must not be null"); if (!isSynchronizationActive()) { throw new IllegalStateException("Transaction synchronization is not active"); } synchronizations.get().add(synchronization); }


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM