2012-02-21 83 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