旧代码如下:
private static final ReentrantLock RE_ENTRANT_LOCK = new ReentrantLock(true);
private void newRunTransaction(final OrderPayment payment, final Address billingAddress, final String command) {
TransactionResponse response = null;
RE_ENTRANT_LOCK.lock();
try {
SoapServerPortType client = getClient();
....
我们认为方法开头的锁是多余的,因为我们应该能够在多个线程中运行事务。另一方面,如果 OrderPayment 与 2 个并行线程中的同一订单相关,则我们无法并行运行事务。
是否有任何优雅且有效的方法来确保与一个订单相关的交易不会并行运行,但所有其他交易都是多线程的?
最佳答案
[更新]:添加了使用 WeakHashMap-Cache 的解决方案,以由垃圾收集器清理未使用的锁。它们是在这里开发的:Iterating a WeakHashMap
如果付款有对其订单的引用,并且相等的订单是相同的对象(order1 == order2 <=> order1 与 order2 相同),则可以使用同步块(synchronized block):
synchronized(payment.getOrder()) {
try {
// ...
}
}
警告:您应该确保 payment.getOrder() 不会产生 null 或在这种情况下使用虚拟对象。
编辑:如果 order1 == order2 不成立,可能的解决方案:
您可以尝试为订单的相同标识符持有唯一锁:
static Map<Long, Object> lockCache = new ConcurrentHashMap<>();
在方法中
Object lock = new Object();
Object oldlock = lockCache.putIfAbsent(payment.getOrder().getUid(), lock);
if (oldlock != null) {
lock = oldlock;
}
synchronized(lock) {
// ...
}
工作完成后,不要忘记拔掉 key 。
要使用垃圾收集来删除未使用的键,您可以使用 WeakHashMap 结构:
private static Map<Long, Reference<Long>> lockCache = new WeakHashMap<>();
public static Object getLock(Longi)
{
Long monitor = null;
synchronized(lockCache) {
Reference<Long> old = lockCache.get(i);
if (old != null)
monitor = old.get();
// if no monitor exists yet
if (monitor == null) {
/* clone i for avoiding strong references
to the map's key besides the Object returend
by this method.
*/
monitor = new Long(i);
lockCache.remove(monitor); //just to be sure
lockCache.put(monitor, new WeakReference<>(monitor));
}
}
return monitor;
}
当您需要更复杂的东西(例如可重入锁)时,您可以使用以下解决方案的变体:
private static Map<Long, Reference<ReentrantLock>> lockCache = new WeakHashMap<>();
private static Map<ReentrantLock, Long> keyCache = new WeakHashMap<>();
public static ReentrantLock getLock(Long i)
{
ReentrantLock lock = null;
synchronized(lockCache) {
Reference<ReentrantLock> old = lockCache.get(i);
if (old != null)
lock = old.get();
// if no lock exists or got cleared from keyCache already but not from lockCache yet
if (lock == null || !keyCache.containsKey(lock)) {
/* clone i for avoiding strong references
to the map's key besides the Object returend
by this method.
*/
Long cacheKey = new Long(i);
lock = new ReentrantLock();
lockCache.remove(cacheKey); // just to be sure
lockCache.put(cacheKey, new WeakReference<>(lock));
keyCache.put(lock, cacheKey);
}
}
return lock;
}
关于java - 如何在多线程JAVA环境中保护对象而不损失性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27946988/