2012-02-21 122 views
6

我有一個singleton Spring bean(默認範圍)。所以,一個實例將被多個線程使用。然而,我對線程安全性有點困惑,顯然所有的Spring bean都是線程安全的,如果它們是無狀態的,但我的bean不是無狀態的,它有各種實例變量,每個請求/其他控制器/類使用不同的實例變量。Spring MVC單線程安全?

這是我的單身豆的開頭:

public class PcrfSimulator { 

private final CustomGxSessionIdCacheImpl gxSessionIdCache = new CustomGxSessionIdCacheImpl(); 
private final PcrfRecord pcrfRec = new PcrfRecord(); 
private final ResponseConditions responseConditions = new ResponseConditions(); 

public CustomGxSessionIdCacheImpl getGxSessionIdCache() { 
    return gxSessionIdCache; 
} 

public ArrayList<Rule> getRules() { 
    return pcrfRec.getRules(); 
} 

所以,上面會被多個線程訪問的領域 - 是否足夠,以紀念這些領域的揮發,或做我必須標記方法哪些訪問他們(有很多不僅在這個類,但其他控制器/類以及)與同步和使用等待/通知等?

非常感謝!

回答

3

volatile沒有幫助。它只會確保值真的更新。

揮發性手段(http://www.javamex.com/tutorials/synchronization_volatile.shtml):

  • 這個變量的值將不會被線程本地緩存:所有讀取和寫入的意志直接去「主要記憶」;
  • 對變量的訪問就好像它被包含在同步塊中一樣,同步於它本身。

製作方法進行同步,如果你的控制流永遠不會退出第一次寫入和上次讀取的共享變量,所有共享變量之間的(外)synchronized塊只會幫助只synchronized塊內訪問使用相同的鎖對象。

所以一般的解決方案是在這種情況下阻止共享變量。使類不可變的一種簡單方法是使用局部變量和方法參數,而不是共享實例變量。


您寫道:「如果Spring bean是無狀態的,但它的線程安全,但我的bean不是無狀態的。」 - 確定主題在上面的段落中討論。

但是從你的代碼是接縫,這不是問題!標記爲final的變量因此它們是不可變的。如果該對象的字段以相同的方式運行(未更新或受到足夠的保護以避免併發修改問題),則您沒有可變共享變量。有時這稱爲「有效無狀態」。這意味着值不會改變。所以這對於併發沒有問題,(因爲併發問題是關於更改值的)。

最後:如果字段(PcrfRecord ...)有效無狀態,則可以在不同線程中使用此示例中的此無效類。 (如果字段PcrfRecord ...不是無狀態的,那麼類PcrfSimulator不能稱爲有效的無狀態) - 但是這對於Spring來說並不重要,它是純Java。

順便說一句:如果你的變量是final你不需要使它們成爲volantile

+0

感謝拉爾夫,但最終不僅僅意味着他們只能被實例化一次?我這樣說是因爲在代碼中最終變量是這樣更新的:gxSessionIdCache.addIpAddress(gxSessionId,ipAddress) - 實際上它們在作爲參數傳遞給不使用最終字段的方法時被更新 - 即doStuff(GxSessionIDCache gxSessionIdCache){... } – Rory 2012-02-21 11:26:38

+0

我認爲最後一個變量的「後面」對象也是「有效的無狀態」 – Ralph 2012-02-21 11:48:28

+0

@Rory:我已經將這一段擴展了一點,以便說清楚。 – Ralph 2012-02-21 11:52:28

3

Spring本身確保在實例化,注入等時確保正確發佈bean。這意味着任何具有對singleton bean的引用的線程至少會看到它的狀態,因爲它在Spring的末尾上下文創建。

如果這個狀態是不變的,那麼你就沒有任何事情要做。

如果單例的狀態是可變的,那麼您將不得不正確地將訪問同步到這個可變狀態。

+0

這個bean的最終初始化字段和獲取者,並不是說這個bean不可能有任何線程安全問題?唯一的問題是字段類是否是線程安全的。 – 2012-02-21 11:53:07

+0

沒錯。豆本身就是好的。它的字段,如果可變的話,必須做成線程安全的。 – 2012-02-21 12:08:34

0

你的類將不會是線程安全的,如果你把它標記爲在上下文中單,因爲你初始化 等領域的「new」手動其作爲創建豆和你將有一個實例在發生一次內存就像你的單例,因此,你的線程共享CustomGxSessionIdCacheImpl,PcrfRecord等實例。

如果你可以讓這些情況下采取下spring環境的控制,如:

<bean id="customGxSessionIdCache" class="package.CustomGxSessionIdCacheImpl" scope="prototype"> 

和自動裝配他們PcrfSimulator,如:

@Autowired 
private final CustomGxSessionIdCacheImpl gxSessionIdCache 

,只要你的代碼訪問上gxSessionIdCache,spring分別爲每個訪問和每個線程創建一個新實例。 Singleton中的任何其他方法都必須使用​​進行標記,因爲這些方法對於多線程的接受是開放的。春天的單身人士是一般的單身人士。

我認爲,如果你根本沒有任何狀態,那麼一切都是線程安全的是錯誤的。如果你認爲低層次,這些方法也有狀態,即局部變量,並且如果有多個線程訪問這些變量,你也會頭痛。

+0

謝謝,但是這不會意味着我將擁有多個緩存實例嗎?我只想要一個緩存,由所有線程訪問和更新。 – Rory 2012-02-21 11:21:29

+0

您必須將緩存的方法標記爲同步。很明顯。 – 2012-02-21 11:27:19

+0

我不明白爲什麼範圍是在上面的例子中的原型,爲什麼不只是保持它的單身? – Rory 2012-02-22 11:06:01

0

由於已經建立,您的班級不是線程安全的。原型範圍是一種可行的方法,但是如果原型範圍的bean自動裝配到單例bean中,它仍然意味着只創建了原型bean的一個實例,並且有效地使其成爲單例。

同步是另一種方式,但這隻有在實例變量爲意味着在線程之間共享時才適用。但是,如果意圖是每個線程的實例變量應該是唯一的,您應該看看ThreadLocal

+0

@ErhanBagdemir你錯了。 Prototype意味着每次將bean注入到另一個bean或使用getBean()明確請求時創建的新實例。見文檔http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-factory-scopes-prototype(特別是4.5.3) – pap 2012-02-21 12:54:07