春天的线程安全会话bean的最佳实践?

时间:2013-10-18 06:31:28

标签: java session spring-mvc thread-safety

我想知道使会话bean线程安全的最佳做法是什么。

我们假设我有这个会话bean及其服务:

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
class Cart {

    private HashSet<Item> items = new HashSet<Item>();

    private int counter = 0;

    public HashSet<Item> getItems() {
        return items;
    }

    public int getCounter() {
        return counter;
    }

}

@Service
class CartService {

    @Autowired
    private Cart cart;

    public void addItem(Item item) throws FullException {
        if (cart.getCounter() > 1234) {
            throw new FullException();

        }
        cart.getItems().add(item);
    }

}

上面的代码不是线程安全的,并且当多个线程(同一个会话,例如异步Ajax请求)执行CartService.addItem(Item)时会导致问题。

我想我不是第一个遇到这个问题的人,但是我的研究并没有带给我最佳实践。

我可以做的最糟糕的事情是同步addItem(),因为CartService由多个会话共享。在CartService.addItem()中同步推车在我看来同样糟糕,因为Cart是一个代理bean。我理解,因为所有会话仍然会在同一个对象上同步。

一个可接受的解决方案似乎是Cart.getItems()CartService.addItem()的同步阻止:

@Service
class CartService {

    @Autowired
    private Cart cart;

    public void addItem(Item item) {
        synchronized(cart.getItems()) {
            if (cart.getCounter() > 1234) {
                throw new FullException();

            }
            cart.getItems().add(item);
        }
    }

}

有最佳做法吗?也许春天可以为这个问题提供一些东西?

2 个答案:

答案 0 :(得分:2)

在Spring API中挖掘了一下后,我发现RequestMappingHandlerAdapter.setSynchronizeOnSession(boolean)似乎同步会话互斥锁上的每个控制器。这可能有点矫枉过正。但它至少使会话中的控制器线程安全,而不会阻塞其他用户,而且我不必担心我的控制器中的同步。但对于高度响应的Ajax-GUI,这仍然是不可接受的。

我觉得这个问题没有一般的答案,完全取决于GUI。如果我有简单的HTML页面,其中预期顺序请求RequestMappingHandlerAdapter.setSynchronizeOnSession(true)似乎是完美的,因为我不必考虑我的控制器中的同步。

如果GUI因大量并行AJAX请求变得更加花哨,我必须通过选择合格的互斥锁来关注同步。

答案 1 :(得分:-1)

首先,我假设您打算说Cart bean是会话作用域(您的示例说它是请求作用域,但这似乎没有意义)和{{1}是一个单身人士。

如果我们考虑用户与应用程序的交互方式,我们是否真的需要在CartService实例上执行任何同步?

我们绝对不需要使Cart同步,因为我们依赖Spring的代理注入来确保在执行线程期间注入当前Session的CartService.addItem()实例,所以不用担心那里。

因此,我们的同步问题是在单个会话期间是否存在需要我们将项目同步添加到Cart的方案?

我认为你的Session是由用户的浏览器驱动的。在不同浏览器之间共享单个会话是没有合法方式的。因此,除了打开多个标签页外,您的用户很可能会以顺序方式向其Cart添加项目。即使他们打开多个标签并输入项目是否有问题?用户启动“结帐”流程后,您可以阻止向Cart添加更多项目,然后让用户确认他们即将结帐的Cart是否正确,让他们有机会删除任何可疑项目。

如果您仍然认为您确实需要Cart上的同步,那么我会执行以下操作:

Cart

通过这种方式,您可以轻松地同步public class Cart { private HashSet<Item> items = new HashSet<Item>(); public synchronized void addItem(Item item) { items.add(item); } } 类中Set的访问权限。