如何在不损失性能的情况下保护多线程JAVA环境中的对象?

时间:2015-01-14 15:49:23

标签: java multithreading locking

遗留代码如下:

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个并行线程中的相同订单相关,那么我们就无法并行运行事务。

是否有任何优雅而有效的方法来确保与一个订单相关的交易不会并行运行,但所有其他交易都是多线程的?

2 个答案:

答案 0 :(得分:2)

[更新] :使用WeakHashMap-Cache添加解决方案,以清除垃圾收集器完成的未使用锁定。它们是在这里开发的:Iterating a WeakHashMap

如果付款有对其订单的引用而且相等的订单是相同的对象(order1 == order2< => order1与order2相同),则可以使用synchronized块:

synchronized(payment.getOrder()) {
    try  {
       // ...
    }
}

警告:您应该确保,对于该情况,payment.getOrder()不会产生null或使用dummy-Objects。

编辑:如果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) {
    // ...
}

完成工作后别忘了取下钥匙。

要使用垃圾收集来删除未使用的密钥,可以使用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;
}

答案 1 :(得分:-1)

如何为每个OrderPayment分配读写锁? http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html

当您只读取订单时,您锁定了readlock,这样多个线程只使用该命令不会相互阻塞。只有在有一个线程修改订单时,其他线程才会被阻止。

我之前还回答了一个读写锁实现: https://stackoverflow.com/a/27869471/1646996