实习字符串上的嵌套同步块

Boz*_*zho 4 java concurrency multithreading synchronization

标题听起来好像前面有很多问题.这是我的具体案例:

这是一个旅游门票销售系统.每条路线的门票数量都是有限的,因此两个人无法访问购买给定路线的最后一张门票(标准方案).但是,有"返回票证"选项..所以,我使用唯一的路径ID(数据库提供)来执行以下操作:

synchronized(bothRoutesUniqueString.intern()) {
    synchronized (routeId.intern()) {
        if (returnRouteId != null) {
            synchronized (returnRouteId.intern()) {
                return doPurchase(selectedRoute, selectedReturnRoute);
            }
        }
        return doPurchase(selectedRoute, selectedReturnRoute);
    }
}
Run Code Online (Sandbox Code Playgroud)

两个内部synchronized块是为了使线程在那里停止,只有当两个人同时购买这个特定路线的票时,而不是同时购买两个不同路线的票.第二次同步是因为有人可能试图同时购买转发路线作为出站路线.

最外面的synchronized区块是考虑场景,当两个人购买相同的门票组合时,逆转.例如,一个订购伦敦 - 曼彻斯特,另一个订购曼彻斯特 - 伦敦.如果没有外部同步块,这种情况可能会导致死锁.

(如果没有可用的票证,该doPurchase()方法返回一个Ticket对象,或者抛出一个异常)

现在,我完全清楚这是一个非常尴尬的解决方案,但是,如果它按预期工作,它会给出:

  • 处理整个复杂场景的10行(并且有适当的注释,它不会那么难理解)
  • 没有不必要的锁定 - 只有当它必须阻塞时,一切都会阻塞.
  • 数据库不可知论

我也知道这些场景是由悲观或乐观的数据库锁处理的,因为我使用的是Hibernate,所以这些也不难实现.

我认为使用VM群集可以使用上面的代码实现水平扩展.根据Teracotta文档,它允许将单节点多线程应用程序转换为多节点,并且:

Terracotta跟踪String.intern()调用并保证这些显式实例化字符串的引用相等性.由于对interned String对象的所有引用都指向规范值,因此即使对于分布式应用程序,引用相等性检查也将按预期工作.

那么,现在问题本身:

  • 你发现上述代码的任何缺点(除了它的尴尬)?
  • java.util.concurrent在这种情况下,API 是否有适用的类来帮助?
  • 为什么数据库锁定比这更好?

更新:由于大多数答案都是关注的OutOfMemoryError,我创建了一个基准测试intern(),并且内存没有被消耗掉.也许字符串表正在被清除,但这在我的情况下并不重要,因为我需要在竞争条件中对象相等,并且清除最近的字符串不应该发生在这一点:

System.out.println(Runtime.getRuntime().freeMemory());
for (int i = 0; i < 10000000; i ++) {
    String.valueOf(i).intern();
}
System.out.println(Runtime.getRuntime().freeMemory());
Run Code Online (Sandbox Code Playgroud)

PS环境是JRE 1.6

T.J*_*der 8

为什么数据库锁定比这更好?

只有在数据库只有一个前端时,您的代码内解决方案才有效,这意味着您只能垂直扩展(在单个前端盒上具有更强的处理能力).一旦开始进入Web场或类似场景(水平扩展),您的解决方案就不再起作用了.