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