6

我想知道使会话 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()似乎同样糟糕,因为购物车是一个代理 bean。据我了解,所有会话仍将在同一个对象上同步。

一种可接受的解决方案似乎是 in 上的同步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);
        }
    }

}

有没有最佳实践?也许春天可以为这个问题提供一些东西?

4

2 回答 2

2

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

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

如果 GUI 因大量并行 AJAX 请求而变得更加花哨,我必须通过选择合格的互斥体来处理同步问题。

于 2013-10-18T15:59:39.067 回答
-1

首先,我假设您的意思是说Cartbean 是 Session 范围的(您的示例说它是请求范围的,但这似乎没有意义)并且CartService是一个单例。

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

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

所以我们的同步问题是关于在单个 Session 期间是否存在需要我们同步添加项目到一个 Session 的场景Cart

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

如果您仍然决定确实需要同步,Cart那么我将执行以下操作:

public class Cart
{
     private HashSet<Item> items = new HashSet<Item>();

     public synchronized void addItem(Item item)
     {
           items.add(item);
     }
}

这样您就可以轻松地同步对类Set内的访问Cart

于 2013-10-18T09:44:13.563 回答