Java 并发 - 如何同步方法参数

Java concurrency - How to synchronize on method parameter

我在 Java 中有一个与同步和并发相关的问题。

所以我有一个方法,像这样:

private boolean loadData(final Integer fundId, List<Trade> trades) {
        synchronized (fundId) {
            // do lots of things here and finally load the trades into DB
        }
    }

在我进行此更改之前,已同步完整方法 loadData private synchronized boolean loadData。但是,我的要求是,如果 fundId - 1 正在处理,那么我可以允许并发处理除 1 以外的任何其他 fundId。

所以,上面的代码也不会工作,因为锁将锁定在 Integer 对象上,因此不能同时处理其他 fundId。 有没有办法根据方法参数实现并发处理?

如所写,该函数将在对象 fundId 上同步,而不是在 Integer 上同步。因此,如果您从具有相同 fundId 实例的另一个线程调用相同的函数,它将阻塞。但是,如果您将它与其他 fundId 个实例一起调用,无论值如何,它都不会同步。

如果您需要基于一个值进行同步,您可以使用一组共享的整数(即 fundId)。在集合上同步,并尝试插入整数。如果它已经在那里,其他人正在处理那个值,所以你等待。如果它不存在,那么你插入它,解锁,处理,再次锁定,删除值,然后发出信号。

您需要在 ConcurrentHashMap 中为每个 fundId 值创建一个条目才能锁定它。

   static Map<Integer, Object> locks = new ConcurrentHashMap<>();
    
   private boolean loadData(final Integer fundId, List<Trade> trades){
        locks.computeIfAbsent(fundId, k-> { /* your actual job */ return null; });
        }
    }

希望对您有所帮助!

您可以通过多种方式实现:

  • 如果包含 loadData() 的 class 被称为 FundLoader 你可以有一个 Map<Integer, FundLoader> fundLoaders 并且每个 FundLoader 负责加载给定 fundId 的交易。 loadData
  • 的同步将再次在方法级别进行
  • 在 loadData 中进行自定义同步 更新 - 添加了 fundsWaitingForLock 以防止锁已从 fundLocks 映射中获取的情况
private final Map<Integer, Object> fundLocks = new HashMap<>();
private final Map<Integer, AtomicInteger> fundsWaitingForLock = new HashMap<>();

private boolean loadData(final Integer fundId, final List<String> trades) {
    Object lock;
    synchronized (fundLocks) {
        lock = fundLocks.computeIfAbsent(fundId, id -> new Object());
        fundsWaitingForLock.computeIfAbsent(fundId, id -> new AtomicInteger()).incrementAndGet();
    }
    synchronized(lock) {
        try {
            // do lots of things here and finally load the trades into DB
            return true;
        } finally {
            synchronized (fundLocks) {
                if (fundsWaitingForLock.get(fundId).decrementAndGet() == 0) {
                    fundLocks.remove(fundId);
                    fundsWaitingForLock.remove(fundId);
                }
            }
        }
    }
}
  • 传递锁而不是 fundId。
private boolean loadData(final Lock fundIdLock, final List<String> trades) {
    fundIdLock.lock();
    try {
        // do lots of things here and finally load the trades into DB
    } finally {
        fundIdLock.unlock();
    }
    return true;
}