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;
}
我在 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;
}