Struts2 会话并发问题



我有一些麻烦。

例如,我有两个操作:第一个和第二个。

我编写了简单的实用程序,它使用执行器服务将 100000 个异步请求发送到操作第一和操作第二。

在第一个操作中,我做:

HitCounter.increment();
ActionContext.getContext().getSession().put("counter", HitCounter.getAtomicCounter());
return Action.SUCCESS;

在第二个动作中,我做:

System.out.println("From session: "+ActionContext.getContext().getSession().get("counter"));
System.out.println("Actual:"+ HitCounter.getAtomicCounter());
return Action.SUCCESS;

我看到的输出(这真的让我生气):

From session: 2 
Actual: 69352

一段时间后,当我仅从浏览器中使用此 Fitst 操作/秒操作并且没有并发请求(由我的加载实用程序生成)时,结果"稳定"为实际值。因此,我有并发问题。

我应该使用一种标准方法来避免 Struts2 中的并发问题吗?

P.S. HitCounter是线程安全的,因为它只包含一个字段,而且是AtomicInteger。

P.P.S. 命中计数器实现:

public class HitCounter {
    private static AtomicInteger counter = new AtomicInteger(0);
    public static void increment() {
        counter.incrementAndGet();
    }
    public static int getAtomicCounter() {
        return counter.get();
    }
}

P.P.P.S.我删除了线程生成();但这并没有帮助。:(

Struts2 操作是线程安全的,因为它们使用线程本地存储...也就是说,有一个从变量到值的每线程映射。因此没有共享的可变对象状态,不需要同步。

但是,不能以这种方式处理诸如会话之类的资源,因此必须小心并发访问。这个问题讨论了这些问题:使用 request.getSession() 作为锁定对象?

根据这个问题:HttpSession 线程是否安全,设置/获取属性线程操作是否安全?servlet 规范声明以下内容是线程安全的:request.getSession().setAttribute("foo", 1);

但是请注意,上面的请求是一个 HttpServletRequest,它反过来派生了一个 HttpSession 对象,这个对象不仅仅是 Struts2 返回的映射......这意味着 struts2 版本必须包装,因此可能不符合规范。因此,我们可以实现 ServletRequestAware,它为我们提供了一个 HttpServletRequest 对象,我们可以从中派生一个 HttpSession。但是,正如前面的问题所指出的那样,这一行对我们没有多大帮助,因此实现HttpServletRequest将是浪费时间。查找所有这些所做的让我想知道 Struts2 如何协调它为会话返回的 Map 与不实现 Map 的 HttpSession 协调,它甚至没有返回映射的方法......

因此,让我们考虑一下目前可能发生的事情......

line1: hitCounter.increment();
line2: ActionContext.getContext().getSession().put("counter", hitCounter.getAtomicCounter());

第 1 行:我们增加一些全局 hitCounter 对象。

第 2a 行:我们得到 hitCounter 对象的副本(int 作为返回类型)。

第 2b 行:我们在会话中设置 hitCounter 值。

由于 HitCounter 的线程安全特性,我们知道第 1 行总是好的......但是第2行呢?我们可以看到有两个部分,如果在获取命中库恩特副本和设置命中计数器之间挂起一个线程,就会有一个竞争条件......从这一点开始执行的最后一个线程将获胜。

一种方法是将 AtomicInteger 放入它自己的会话中,这样可以避免副本滑入的问题。

您没有在第二个操作中检索命中计数器。

System.out.println("From session: "+ActionContext.getContext().getSession().get("counter"));

上面的行只是打印对象。代码中缺少某些内容吗?

最新更新