繁体   English   中英

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

[英]How to protect an object in multithreaded JAVA environment without losing performance?

遗留代码如下:

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的解决方案,以清理垃圾收集器要使用的未使用锁。 它们是在这里开发的: 迭代WeakHashMap

如果付款参考其订单,并且相等的订单是相同的对象(order1 == order2 <=> order1与order2相同),则可以使用同步块:

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) {
    // ...
}

完成工作后,请不要忘记删除密钥。

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

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

当您仅读取命令时,您将锁定读取锁,这样,仅使用该命令的多个线程将不会相互阻塞。 仅当有一个线程修改顺序时,其他线程才会被阻塞。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM