1

我有一些麻烦。

例如,我有两个动作:First 和 Second。

我编写了简单的实用程序,它使用执行器服务将 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 中的并发问题吗?

PS HitCounter 是线程安全的,因为它只包含一个字段并且是 AtomicInteger。

PPS HitCounter 实现:

public class HitCounter {
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void increment() {
        counter.incrementAndGet();
    }
    public static int getAtomicCounter() {
        return counter.get();
    }
}

PPPS 我删除了 Thread.yield(); 但这没有帮助。:(

4

2 回答 2

1

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

但是,不能以这种方式处理诸如 Session 之类的资源,因此必须注意并发访问。这个问题讨论了这些问题:Using request.getSession() as a locking object?

根据这个问题: Is HttpSession thread safe, are set/get Attribute thread safe operations?servlet 规范声明以下是线程安全的:request.getSession().setAttribute("foo", 1);

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

所以让我们考虑一下目前可能发生的事情......

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

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

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

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

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

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

于 2012-04-25T09:41:48.180 回答
0

您没有在第二个操作中检索 HitCounter。

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

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

于 2012-04-25T06:45:18.157 回答