1. 程式人生 > >Spring併發訪問的執行緒安全性問題(高度總結)

Spring併發訪問的執行緒安全性問題(高度總結)

spring中的併發訪問題:

我們知道在一般情況下,只有無狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域。
那麼對於有狀態的bean呢?Spring對一些(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非執行緒安全狀態的bean採用ThreadLocal進行處理,讓它們也成為執行緒安全的狀態,因此有狀態的Bean就可以在多執行緒中共享了。

如果用有狀態的bean,也可以使用用prototype模式,每次在注入的時候就重新建立一個bean,在多執行緒中互不影響。



有狀態就是有資料儲存功能。有狀態物件(Stateful Bean),就是有例項變數的物件 ,可以儲存資料,是非執行緒安全的。在不同方法呼叫間不保留任何狀態。 無狀態就是一次操作,不能儲存資料。無狀態物件(Stateless Bean),就是沒有例項變數的物件 .不能儲存資料,是不變類,是執行緒安全的。 無狀態的Bean適合用不變模式,技術就是單例模式,這樣可以共享例項,提高效能。

有狀態的Bean,多執行緒環境下不安全,那麼適合用Prototype原型模式。Prototype: 每次對bean的請求都會建立一個新的bean例項。

Servlet體系結構是建立在Java多執行緒機制之上的,它的生命週期是由Web 容器負責的。一個Servlet類在Application中只有一個例項存在,也就是有多個執行緒在使用這個例項。這是單例模式的應用。
如Service層、Dao層用預設singleton就行,雖然Service類也有dao這樣的屬性,但dao這些類都是沒有狀態資訊的,也就是相當於不變(immutable)類,所以不影響。Struts2中的Action因為會有User、BizEntity這樣的例項物件,是有狀態資訊的,在多執行緒環境下是不安全的,所以Struts2預設的實現是Prototype模式。在Spring中,Struts2的Action中,scope要配成prototype作用域。 (單例模式-單例登錄檔實現和threadLocal-可以處理有狀態的bean之間的關係)

還有我們的實體bean,從客戶端傳遞到後臺的controller-->service-->Dao,這一個流程中,他們這些物件都是單例的,那麼這些單例的物件在處理我們的傳遞到後臺的實體bean不會出問題嗎?
答:[實體bean不是單例的],並沒有交給spring來管理,每次我們都手動的New出來的【如EMakeType et = new EMakeType();】,所以即使是那些處理我們提交資料的業務處理類是被多執行緒共享的,但是他們處理的資料並不是共享的,資料時每一個執行緒都有自己的一份,所以在資料這個方面是不會出現執行緒同步方面的問題的。

(在這裡補充下自己在專案開發中對於實體bean在多執行緒中的處理:1。對於實體bean一般通過方法引數的的形式傳遞(引數是區域性變數),所以多執行緒之間不會有影響。2.有的地方對於有狀態的bean直接使用prototype原型模式來進行解決。3.對於使用bean的地方可以通過new的方式來建立)

但是那些的在Dao中的xxxDao,或controller中的xxxService,這些物件都是單例那麼就會出現執行緒同步的問題。但是話又說回來了,這些物件雖然會被多個程序併發訪問,可我們訪問的是他們裡面的方法,這些類裡面通常不會含有成員變數,那個Dao裡面的ibatisDao是框架裡面封裝好的,已經被測試,不會出現執行緒同步問題了。所以出問題的地方就是我們自己系統裡面的業務物件,所以我們一定要注意這些業務物件裡面千萬不能要獨立成員變數,否則會出錯。

spring對那些個有狀態bean使用ThreadLocal維護變數[僅僅是變數,因為執行緒同步的問題就是成員變數的互斥訪問出問題]時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。

對spring併發訪問執行緒安全的兩篇部落格彙總,可以得出上述結論..............

由於Spring MVC預設是Singleton的,所以會產生一個潛在的安全隱患。根本核心是instance的變數保持狀態的問題。這意味著每個request過來,系統都會用原有的instance去處理,這樣導致了兩個結果(單例的好處):

一是我們不用每次建立Controller,
二是減少了物件建立和垃圾收集的時間;
由於只有一個Controller的instance,當多個執行緒同時呼叫它的時候,它裡面的instance變數(可以理解為私有變數)就不是執行緒安全的了,會發生竄資料的問題。
當然大多數情況下,我們根本不需要考慮執行緒安全的問題,比如dao,service等,除非在bean中聲明瞭例項變數。因此,我們在使用spring mvc 的contrller時,應避免在controller中定義例項變數(singleton唯一的不好是單例的變數容易出現問題,下面有解決的方案)
如:

publicclassControllerextendsAbstractCommandController{
......
protectedModelAndView handle(HttpServletRequest request,HttpServletResponse response,
Object command,BindException errors)throwsException{
company =................;
}
protectedCompany company;
}
在這裡有宣告一個變數company,這裡就存在併發執行緒安全的問題。
如果控制器是使用單例形式,且controller中有一個私有的變數a,所有請求到同一個controller時,使用的a變數是共用的,即若是某個請求中修改了這個變數a,則,在別的請求中能夠讀到這個修改的內容。。

有幾種解決方法:
1、在控制器中不使用例項變數(可以使用方法引數的形式解決,參考博文)
2、將控制器的作用域從單例改為原型,即在spring配置檔案Controller中宣告 scope="prototype",每次都建立新的controller
3、在Controller中使用ThreadLocal變數

這幾種做法有好有壞,第一種,需要開發人員擁有較高的程式設計水平與思想意識,在編碼過程中力求避免出現這種BUG,

而第二種則是容器自動的對每個請求產生一個例項,由JVM進行垃圾回收,因此做到了執行緒安全。

使用第一種方式的好處是例項物件只有一個,所有的請求都呼叫該例項物件,速度和效能上要優於第二種,不好的地方,就是需要程式設計師自己去控制例項變數的狀態保持問題。第二種由於每次請求都建立一個例項,所以會消耗較多的記憶體空間。

所以在使用spring開發web 時要注意,預設Controller、Dao、Service都是單例的。

【1】SpringMVC多執行緒環境中如何保證物件的安全性?
程式碼如下:

@RequestMapping("/user") @Controller ClassUserController { @Resource UserService userService; @RequestMapping("/add") publicvoid testA(User user){ userService.add(user); } @RequestMapping("/get") publicvoid testA(int id){ userService.get(id); } } @Service("userService") ClassUserService{ publicstaticMap<Integer,User> usersCache =newHashMap<String,User>(); publicvoid add(User user){ usersCache.put(user.getId(),user); } publicvoidget(int id){ usersCache.get(id); } }

此段程式碼,usersCache物件就是執行緒不安全的。因為它是靜態的全域性共享物件。如果有多個執行緒同時呼叫add方法,可能會發生使用者物件被覆蓋的情況,也就是id對應物件不一致,這是多執行緒程式設計中最常發生的事情。 所以,可以使用 Collections 工具同步Map。 static Map<Integer, Users> usersCache = Collections.synchronizedMap(new HashMap<Integer, Users>()); 研究一下,Spring中的原始碼,它對常用的開源框架做了大量封裝,如,Hibernate中的sessionFactory,就使用的是 org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean,而在 AnnotationSessionFactoryBean的父類LocalSessionFactoryBean中,定義了大量的ThreadLocal來保證多執行緒的安全性。

publicclassLocalSessionFactoryBeanextendsAbstractSessionFactoryBeanimplementsBeanClassLoaderAware{ privatestaticfinalThreadLocal<DataSource> configTimeDataSourceHolder = newThreadLocal<DataSource>(); privatestaticfinalThreadLocal<TransactionManager> configTimeTransactionManagerHolder = newThreadLocal<TransactionManager>(); privatestaticfinalThreadLocal<Object> configTimeRegionFactoryHolder = newThreadLocal<Object>(); privatestaticfinalThreadLocal<CacheProvider> configTimeCacheProviderHolder = newThreadLocal<CacheProvider>(); privatestaticfinalThreadLocal<LobHandler> configTimeLobHandlerHolder = newThreadLocal<LobHandler>();

}


相關推薦

Spring併發訪問執行安全性問題高度總結

spring中的併發訪問題: 我們知道在一般情況下,只有無狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域。 那麼對於有狀態的bean呢?Spring對一些(如RequestContextHolder、Tra

Spring 中的執行安全性

Spring與執行緒安全   Spring作為一個IOC/DI容器,幫助我們管理了許許多多的“bean”。但其實,Spring並沒有保證這些物件的執行緒安全,需要由開發者自己編寫解決執行緒安全問題的程式碼。   Spring對每個bean提供了一個s

淺談Semaphore類 如何控制某個方法允許併發訪問執行的個數?

Semaphore類有兩個重要方法 1、semaphore.acquire(); 請求一個訊號量,這時候訊號量個數-1,當減少到0的時候,下一次acquire不會再執行,只有當執行一個release()的時候,訊號量不為0的時候才可以繼續執行acquire 2、semaphore.release();

Java併發程式設計2執行中斷含程式碼

使用interrupt()中斷執行緒當一個執行緒執行時,另一個執行緒可以呼叫對應的Thread物件的interrupt()方法來中斷它,該方法只是在目標執行緒中設定一個標誌,表示它已經被中斷,並立即返回。這裡需要注意的是,如果只是單純的呼叫interrupt()方法,執行緒並沒有實際被中斷,會繼續往下執行。

java併發學習--執行

關於java中的執行緒池,我一開始覺得就是為了避免頻繁的建立和銷燬執行緒吧,先建立一定量的執行緒,然後再進行復用。但是要具體說一下如何做到的,自己又說不出一個一二三來了,這大概就是自己的學習習慣流於表面,不經常深入的結果吧。所以這裡決定系統的學習一下執行緒池的相關知識。   自己稍微總結了一下,

Spring中的執行安全性

一:Spring與執行緒安全 Spring作為一個IOC/DI容器,幫助我們管理了許許多多的“bean”。但其實,Spring並沒有保證這些物件的執行緒安全,需要由開發者自己編寫解決執行緒安全問題的程式碼。 Spring對每個bean提供了一個scope屬性來表示該bean的作用域。它是be

併發程式設計---執行封閉棧封閉和ThreadLocal

    在多執行緒環境中,如果多個執行緒同時訪問一個共同的可變變數,我們稱這個變數為共享變數,如果不做任何措施,很可能會引起執行緒安全問題。那麼如何解決這個執行緒安全問題是我們要考慮的。我們通常的做法是使用同步,但是我們有沒有想過執行緒的安全問題是因為共享,那

Java併發基礎—執行安全性

執行緒安全性         當多個執行緒訪問某個類時,不管執行時環境採用何種排程方式或者這些程序如何交替執行,並且在主調程式碼中無需任何額外的同步或協同,這個類都能表現出正確的行為,那麼就稱這個類是執行緒安全的。 執行緒安全性——原子性         提供了互斥訪問

C++11 併發與多執行未完成

從C++11新標準開始,C++語言本身增加了對多執行緒的支援,意味著使用C++可實現多執行緒程式的可移植,跨平臺。 在標準的C++程式中,主執行緒從main()開始執行,我們自己在C++中建立的執行緒,也需要從一個函式開始執行(這個函式叫做初始函式),一旦這個函式執行完

Java併發程式設計(8)-使用閉鎖測試併發執行安全性

本文將介紹什麼是閉鎖,在java中的閉鎖實現:CountDownLatch類及其常用方法等,最後給出了一個使用閉鎖模擬執行緒併發的demo,用以簡單地測試任務是否為執行緒安全。 一、什麼是閉鎖 閉鎖(Latch)是在併發程式設計中常被提及的概念。閉鎖是一

聊下併發和Tomcat執行錯誤更正

本文前半部分結論存在嚴重錯誤,請看最後2015-1-20更新部分。 最近一直在解決線上一個問題,表現是: Tomcat每到凌晨會有一個高峰,峰值的併發達到了3000以上,最後的結果是Tomcat執行緒池滿了,日誌看很多請求超過了1s。 伺服器效能很好,Tomcat版本是7.0.54,配置如下

Guava併發:RateLimiter限制資源的併發訪問執行

RateLimiter類似於JDK的訊號量Semphore,他用來限制對資源併發訪問的執行緒數。 RateLimiter limiter = RateLimiter.create(4.0); //每秒不超過4個任務被提交 limiter.acquire();  //請求Ra

JAVA 併發程式設計-執行建立

對於執行緒的建立及更加詳細的資訊可以參看部落格《執行緒》,下面是對執行緒建立的細化及簡單再實現。在java中如果要建立執行緒的話,一般有兩種方式:1)繼承Thread類;2)實現Runnable介面。方

Spring MVC Controller執行安全性問題

Spring MVC預設是單例模式,Controller、Service、Dao都是單例所以在使用不當存在一定的安全隱患。Controller單例模式的好處在與: 1. 提高效能,不用每次建立Con

Java併發程式設計--執行封閉Ad-hoc、棧、ThreadLocal

執行緒封閉(Thread Confinement):僅在單執行緒內訪問資料,不需要同步。 常見應用是:JDBC(Java Database Connectivity)的Connection物件。 1.Ad-hoc執行緒封閉  指維護執行緒封閉性的職責完全由程式實現來承擔。A

併發新特性—Executor框架與執行含程式碼

Executor框架簡介 在Java 5之後,併發程式設計引入了一堆新的啟動、排程和管理執行緒的API。Executor框架便是Java 5中引入的,其內部使用了執行緒池機制,它在java.util.cocurrent 包下,通過該框架來控制執行緒的啟動、執行

Java多執行學習基礎篇

1. java對執行緒的支援 java對執行緒的支援主要體現在Thread類以及Runable介面上,他們都位於java.lang包下,無論是Thread類還是Runable介面,它們都有public void run()方法,這個run方法為我們提供了執行緒實際工作時的程式碼,換句

【python3】多執行-執行非同步推薦使用

- python3有threading和_thread兩種執行緒寫法,推薦使用threading。 開多執行緒就是為了使用多執行緒的非同步能力來同時執行多個執行緒。 1. threading方法 #!/usr/bin/python3 # 執行緒非同步 import thread

4-5 執行安全性-有序性與總結

有序性 一個執行緒觀察其他執行緒中的指令執行順序,由於指令重排序的存在,該觀察結果一般雜亂無序。 JMM允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單執行緒程式的執行,卻會影響到多執行緒併發執行的正確性。 可以通過volatile、synchro

iOS多執行筆記GCD理解

0x00 先上腦圖 0x01 iOS的三種多執行緒技術 1.NSThread 每個NSThread物件對應一個執行緒,量級較輕(真正的多執行緒)2.以下兩點是蘋果專門開發的“併發”技術,使得程式設計師可以不再去關心執行緒的具體使用問題ØNSOperation/NSOperationQueue 面向物件的