有关的更多详细信息并发访问异常 http://docs.oracle.com/javaee/6/api/javax/ejb/ConcurrentAccessException.html:根据 EJB 规范,对 SLSB 的访问由应用程序同步。服务器。然而,SFSB 的情况并非如此。确保 SFSB 不会被同时访问的重担落在了应用程序开发人员的肩上。
为什么?那么,SLSB 的同步仅在实例级别是必要的。也就是说,SLSB 的每个特定实例都是同步的,但您可能在池中或集群中的不同节点上有多个实例,并且不同实例上的并发请求不是问题。遗憾的是,由于实例的钝化/激活以及跨集群的复制,这对于 SFSB 来说并不那么容易。这就是为什么规范不强制执行这一点。看一下这次讨论 https://jira.jboss.org/jira/browse/JBAS-1443如果您对该主题感兴趣。
这意味着从 servlet 使用 SFSB 很复杂。用户在同一会话中拥有多个窗口,或者在渲染完成之前重新加载页面可能会导致并发访问。理论上,在 servlet 中完成的对 EJB 的每次访问都需要在 bean 本身上进行同步。我所做的是创建一个调用处理程序 http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/InvocationHandler.html同步特定 EJB 实例上的所有调用:
public class SynchronizationHandler implements InvocationHandler {
private Object target; // the EJB
public SynchronizationHandler( Object bean )
{
target = bean;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
synchronized( target )
{
// invoke method to the target EJB
}
}
}
然后,在获得对 EJB 的远程引用后,立即用SynchronizationHandler
。这样,您就可以确保您的应用程序不会同时访问该特定实例(只要它仅在一个 JVM 中运行)。您还可以编写一个常规包装类来同步 bean 的所有方法。
尽管如此,我的结论是:尽可能使用 SLSB。
EDIT:
这个答案反映了 EJB 3.0 规范(第 4.3.13 节):
不允许客户端对有状态会话进行并发调用
目的。如果客户端调用的业务方法正在进行
例如,当另一个客户端调用来自相同或不同的调用时
客户端,到达有状态会话 Bean 类的同一个实例,
如果第二个客户端是 bean 业务接口的客户端,则
并发调用可能会导致第二个客户端接收到
javax.ejb.ConcurrentAccessException
EJB 3.1(第 4.3.13 节)中删除了此类限制:
默认情况下,允许客户端并发调用有状态的
会话对象和容器需要序列化这样的
并发请求。
[...]
Bean 开发人员可以选择指定并发客户端
禁止向有状态会话 bean 发出请求。这是使用完成的
@AccessTimeout 注释或访问超时部署描述符
值为0的元素。此时,如果客户端调用的业务
当另一个客户端调用时,方法正在实例上进行,
来自相同或不同的客户端,到达同一实例
有状态会话 Bean,如果第二个客户端是该 Bean 的客户端
业务接口或无接口视图、并发调用
必须导致第二个客户端收到
javax.ejb.ConcurrentAccessException