Java 原子地抓取多个锁

Java 原子地抓取多个锁,java,multithreading,concurrency,Java,Multithreading,Concurrency,假设我们必须在任何两个账户之间(在那里的hunders之间)进行转账,作为交易的一部分。 在典型的多线程环境中,会有多个类似的事务同时运行。 通常惯例如下(按照预先设计的惯例维护锁的顺序): 是否有任何方法可以尝试将锁和释放作为原子操作?是的,有:您需要在锁下锁定锁。换句话说,您需要创建一个锁层次结构。但是这个解决方案不是很有效,因为它降低了锁的粒度 在您的情况下,看起来总是以相同的顺序获取锁就足够了。例如,总是先用较小的ID锁定用户。事务是ACID定义的原子(A-表示原子性)。隔离(至少REA

假设我们必须在任何两个账户之间(在那里的hunders之间)进行转账,作为交易的一部分。
在典型的多线程环境中,会有多个类似的事务同时运行。
通常惯例如下(按照预先设计的惯例维护锁的顺序):


是否有任何方法可以尝试将锁和释放作为原子操作?

是的,有:您需要在锁下锁定锁。换句话说,您需要创建一个锁层次结构。但是这个解决方案不是很有效,因为它降低了锁的粒度


在您的情况下,看起来总是以相同的顺序获取锁就足够了。例如,总是先用较小的ID锁定用户。

事务是ACID定义的原子(A-表示原子性)。隔离(至少
READ\u committed
one)保证在完成之前启动的事务时,帐户A可能同时发生的其他事务将等待。所以,实际上,您不需要显式地锁定它们,因为它们将通过内部实现(例如数据库)进行锁定,而且由于可以使用乐观锁定技术,因此锁定将更加有效

但只有当它们都参与一个事务上下文时(例如在
JTA
环境中),这才是正确的。在这种环境下,您可以在转账方法的开始处启动交易,而无需锁定账户A和账户B

如果它们不在同一事务上下文中,您可以引入另一个锁定对象,但这将显著降低性能,因为即使一个线程使用帐户A和B,另一个线程使用帐户C和D,线程也会被锁定。有一些技术可以避免这种情况(例如,请参见
concurrenthashmap
,其中 锁位于篮子上,而不是整个对象上)


但对于您的特定示例,答案可能只是一些一般性的想法,因为示例是简短的,以供进一步研究。我认为变量以特定顺序锁定帐户A和帐户B(应该非常小心,因为这可能会导致潜在的死锁。并且假设不只有传输方法可以处理它们,这是非常高的风险)对于给定的情况来说是正常的。

您可以尝试使用以下代码。 注意:它只适用于两个锁,我不确定如何将其扩展到更多锁。 这个想法是,你拿第一把锁,然后试着拿第二把。 如果失败,我们知道现在有一个锁是空闲的,但另一个锁是忙的。 因此,我们释放第一个锁,您将其反转,因此我们将锁定忙碌的锁,并尝试获取空闲的锁,如果它仍然空闲的话。 冲洗并重复。 从统计上看,该代码不可能进入StackOverflow, 我认为处理它并给出一个错误比让它循环要好,因为这将是一个信号,表明某个地方出了很大的问题

public static void takeBoth(ReentrantLock l1,ReentrantLock l2) {
    l1.lock();
    if(l2.tryLock()) {return;}
    l1.unlock();
    try{takeBoth(l2,l1);}
    catch(StackOverflowError e) {throw new Error("??");}
  }
  public static void releaseBoth(ReentrantLock l1,ReentrantLock l2){
    if(!l1.isHeldByCurrentThread()) {l1.unlock();}//this will fail: IllegarMonitorState exception
    l2.unlock();//this may fail, in that case we did not touch l1.
    l1.unlock();    
    }

我很确定OP使用了transaction这个词来表示传输。这个问题不是关于数据库或分布式事务。@Oleg是的,它不是。它是关于在某个方法上获得原子锁。我只是指出,在事务中,这实际上甚至是不必要的(数据库和分布式事务只是示例,而不是答案的主题)。在非事务性环境中,唯一的方法是引入另一个锁定对象,因此答案在锁定对象中给出。只需要与作者或其他人相关的附加信息。那么这有什么错呢?不知道,可能什么都没有,只是觉得有点不相关。你说服了我删除否决票。显然我除非您编辑答案,否则无法执行此操作。@Oleg我理解它可能看起来不相关-下次将考虑这一点。我是被qestion中提到的事务触发的。(我编辑了我的答案:)听起来像是XY的问题。这是典型的死锁情况,当其他人以相反的顺序锁定时。原子与否。
public static void takeBoth(ReentrantLock l1,ReentrantLock l2) {
    l1.lock();
    if(l2.tryLock()) {return;}
    l1.unlock();
    try{takeBoth(l2,l1);}
    catch(StackOverflowError e) {throw new Error("??");}
  }
  public static void releaseBoth(ReentrantLock l1,ReentrantLock l2){
    if(!l1.isHeldByCurrentThread()) {l1.unlock();}//this will fail: IllegarMonitorState exception
    l2.unlock();//this may fail, in that case we did not touch l1.
    l1.unlock();    
    }