Java 围绕参数值的同步方法

Java synchronized method around parameter value

考虑以下方法:

public void upsert(int customerId, int somethingElse) {
  // some code which is prone to race conditions    
}

我想保护此方法免受竞争条件的影响,但这只有在具有相同 customerId 的两个线程同时调用它时才会发生。如果我使整个方法 synchronized ,它会降低效率并且它不是真正需要的。我真正想要的是围绕 customerId 同步它。 Java 是否有可能以某种方式实现?是否有任何内置工具,或者我需要 Map of Integers 用作锁?

如果您认为我在这里做错了什么,也请随时提出建议:)

谢谢!

您正在寻找的概念称为分段锁定条带锁定。为每个客户单独配一把锁太浪费了(锁是相当重量级的)。相反,您希望将您的客户 ID 分区 space 分成合理数量的分区,以匹配所需的并行度。通常 8-16 就足够了,但这取决于方法所做的工作量。

这里概述了一个简单的方法:

private final Object[] locks = new Object[8];

synchronized (locks[customerId % locks.length]) {
    ...implementation...
}
    private static final Set<Integer> lockedIds = new HashSet<>();

    private void lock(Integer id) throws InterruptedException {
        synchronized (lockedIds) {
            while (!lockedIds.add(id)) {
                lockedIds.wait();
            }
        }
    }

    private void unlock(Integer id) {
        synchronized (lockedIds) {
            lockedIds.remove(id);
            lockedIds.notifyAll();
        }
    }

    public void upsert(int customerId) throws InterruptedException {
        try {
            lock(customerId);

            //Put your code here.
            //For different ids it is executed in parallel.
            //For equal ids it is executed synchronously.

        } finally {
            unlock(customerId);
        }
    }
  • id 不仅可以是 'Integer',还可以是任何 class 并正确覆盖 'equals' 和 'hashCode' 方法。
  • try-finally - 非常重要 - 即使您的操作抛出异常,您也必须保证在操作后解锁等待线程。
  • 如果您的后端分布在 多个 servers/JVMs.
  • 中,它将无法工作