基于方法参数的方法调用的细粒度同步/锁定

时间:2013-05-30 15:02:00

标签: java concurrency locking

我想基于某个id同步方法调用,即类似给定对象实例的并发装饰器 例如:
用param“id1”调用方法的所有线程应该相互串行执行 所有其余的,使用不同的参数调用方法,说“id2”,应该与使用param“id1”调用方法的线程并行执行,但是再次串行相互执行。

所以在我看来,这可以通过为每个这样的方法参数设置一个锁(http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html)实例来实现。 每次使用param调用该方法时,将查找对应于特定参数值(例如“id1”)的锁实例,并且当前线程将尝试获取锁。

在代码中说:

public class ConcurrentPolicyWrapperImpl implements Foo {

private Foo delegate;

/**
* Holds the monitor objects used for synchronization.
*/
private Map<String, Lock> concurrentPolicyMap = Collections.synchronizedMap(new HashMap<String, Lock>());

/**
* Here we decorate the call to the wrapped instance with a synchronization policy.
*/
@Override
public Object callFooDelegateMethod (String id) {
        Lock lock = getLock(id);
        lock.lock();
        try {
            return delegate.delegateMethod(id);
        } finally {
            lock.unlock();
        }
}


protected Lock getLock(String id) {
        Lock lock = concurrentPolicyMap.get(id);

        if (lock == null) {
            lock = createLock();
            concurrentPolicyMap.put(id, lock);

        }
        return lock;
    }

}

protected Lock createLock() {
        return new ReentrantLock();
    }

看起来这很有效 - 我用jmeter做了一些性能测试等等。 尽管如此,我们都知道Java中的并发性是一件棘手的事情,但我决定在这里征求你的意见。

我不禁想到可能有更好的方法来实现这一目标。例如,通过使用BlockingQueue实现之一。你觉得怎么样?

我也无法确定是否存在获取锁定的潜在同步问题,即protected Lock getLock(String id)方法。我正在使用同步集合,但这就够了吗?即不应该像以下那样,而不是我现在拥有的东西:

protected Lock getLock(String id) {
  synchronized(concurrentPolicyMap) {
    Lock lock = concurrentPolicyMap.get(id);

    if (lock == null) {
      lock = createLock();
      concurrentPolicyMap.put(id, lock);

    }
    return lock;
  }

}

那你们觉得怎么样?

2 个答案:

答案 0 :(得分:3)

除了锁定创建问题之外,模式还可以,除了你可能有无限数量的锁。通常人们通过创建/使用条带锁来避免这种情况。番石榴库中有一个很好/简单的实现。

Application area of lock-striping

How to acquire a lock by a key

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/Striped.html

使用guava实现的示例代码:

private Striped<Lock> STRIPPED_LOCK = Striped.lock(64);

public static void doActualWork(int id) throws InterruptedException {
    try {
        STRIPPED_LOCK.get(id).lock();
        ...
    } finally {
        STRIPPED_LOCK.get(id).unlock();
    }
}

答案 1 :(得分:1)

虽然我个人更喜欢Keith建议的Guava的Striped<Lock>方法,但仅供参考。完整性,我想指出使用动态代理或更通用的AOP(面向方面​​编程)是一种方法。

因此,我们将定义一个IStripedConcurrencyAware接口,它将作为您所需的“并发装饰器”,并且基于此接口的动态代理/ AOP方法劫持会将方法调用解复用为适当的Executor / Thread

我个人不喜欢AOP(或Spring的大部分内容)因为它打破了你所看到的Core Java的简单性,但YMMV。