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

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

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

最佳答案

[更新]:添加了使用 Wea​​kHashMap-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 。

要使用垃圾收集来删除未使用的键,您可以使用 Wea​​kHashMap 结构:

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/

相关文章:

java - 在Web应用程序中使用不同的线程进行处理

c++ - 如何轻松使 std::cout 线程安全?

python-2.7 - 有没有办法在不同进程(不是线程)之间同步写入文件

mysql - perl 和 MySQL 中的互斥量

javascript - 如何使用 javaScript 获取从 java 类传递到 native 脚本 jar 文件的变量数据

java - 实例初始化 block 的编译顺序

java - 从局部变量创建 ByteArrayInputStream 是否有效?

java - 如何编写可中断的方法

java - 如何减少以下代码的 "Cyclomatic Complexity"

iphone - 如何从不同线程更改 Core Data 对象,而无需在每次更改后保存