我有一个用于处理付款的ObjectManager类。它包含在Order实体上,因此在需要处理时必须创建新实例。当几个ObjectManager实例同时处理相同的订单时,我需要防止这种情况(它由于远程支付处理中心的一些错误而发生一次,不知何故他们称我们的回调网址两次)。我想知道如何更有效地实施它。
现在,我正在考虑这样的事情:
public class OrderManager{
private static final CopyOnWriteArrayList<Integer> LOCKER =
new CopyOnWriteArrayList<Integer>();
private static synchronized boolean tryLock(Integer key) {
return LOCKER.addIfAbsent(key);
}
private static void releaseLock(Integer key) {
LOCKER.remove(key);
}
public void processPayment(Integer orderId) throws Exception{
if (!tryLock(orderId)) {
return;
}
try {
//operate
} finally {
releaseLock(orderId);
}
}
//remainder omitted
}
答案 0 :(得分:1)
使用CopyOnWriteArrayList对于读取来说是便宜的,但对于写入来说很昂贵,因为你似乎主要是写入,我建议使用O(1)进行写入的集合。
e.g。
private static final Set<Integer> LOCKER = Collections.newSetFromMap(
new ConcurrentHashMap<Integer, Boolean>());
private static boolean tryLock(Integer key) {
return LOCKER.add(key);
}
private static void releaseLock(Integer key) {
LOCKER.remove(key);
}
答案 1 :(得分:1)
我最近实现了一个通用的多锁处理程序机制,可能适合你。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* A synchronization utility for improving lock granularity.
*
* The class is suitable when some operation is associated with an id, and we don't want different
* threads to perform the operation on the same id concurrently. Concurrent executions on different ids is ok.
*
* @param K - the type of the id
*
* @author eschneider
*/
public class MultiLock <K>{
private ConcurrentHashMap<K, ReentrantLock> locks = new ConcurrentHashMap<K, ReentrantLock>();
/**
* Locks on a given id.
* Make sure to call unlock() afterwards, otherwise serious bugs may occur.
* It is strongly recommended to use try{ }finally{} in order to guarantee this.
* Note that the lock is re-entrant.
* @param id The id to lock on
*/
public void lock(K id) {
while (true) {
ReentrantLock lock = getLockFor(id);
lock.lock();
if (locks.get(id) == lock)
return;
else // means that the lock has been removed from the map by another thread, so it is not safe to
// continue with the one we have, and we must retry.
// without this, another thread may create a new lock for the same id, and then work on it.
lock.unlock();
}
}
/**
* Tries locking on a given id, giving up if somebody else has the lock.
* Make sure to call unlock() later in case that the lock is acquired, otherwise serious bugs may occur.
* It is strongly recommended to use try{ }finally{} in order to guarantee this. Note that the lock is re-entrant.
* @param id The id to lock on
* @return true iff the lock has been acquired
*/
public boolean tryLock(K id) {
while (true) {
ReentrantLock lock = getLockFor(id);
if (!lock.tryLock())
return false;
if (locks.get(id) == lock)
return true;
else // means that the lock has been removed from the map by another thread, so it is not safe to
// continue with the one we have, and we must retry.
// without this, another thread may create a new lock for the same id, and then work on it.
lock.unlock();
}
}
/**
* Unlocks on a given id.
* If the lock is not currently held, an exception is thrown.
*
* @param id The id to unlock
* @throws IllegalMonitorStateException in case that the thread doesn't hold the lock
*/
public void unlock(K id) {
ReentrantLock lock = locks.get(id);
if (lock == null || !lock.isHeldByCurrentThread())
throw new IllegalMonitorStateException("Lock for " + id + " is not owned by the current thread!");
locks.remove(id);
lock.unlock();
}
/**
* Gets/creates a lock for a given id. If many threads try it on the same time,
* they will all get the same lock.
* @param id The id
* @return The lock corresponding to the given id
*/
private ReentrantLock getLockFor(K id) {
ReentrantLock lock = locks.get(id);
if (lock == null) {
lock = new ReentrantLock();
ReentrantLock prevLock = locks.putIfAbsent(id, lock);
if (prevLock != null)
lock = prevLock;
}
return lock;
}
}
答案 2 :(得分:0)
我想我会在数据库级别实现锁定 - 所以如果ObjectManager在多个进程中运行,它仍然会识别锁定。