6

我想在一些 id 的基础上同步方法调用,例如给定对象实例的并发装饰器。
例如:
使用参数“id1”调用该方法的所有线程应该彼此串行执行。
其余所有调用具有不同参数的方法(例如“id2”)应该与调用参数“id1”的方法的线程并行执行,但又彼此串行执行。

所以在我看来,这可以通过每个这样的方法参数有一个锁(http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html)实例来实现。每次使用参数调用方法时,都会查找与特定参数值(例如“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;
  }

}

那你们怎么看?

4

2 回答 2

4

撇开锁创建问题不谈,除了您可能拥有无限数量的锁之外,该模式还可以。通常人们通过创建/使用条带锁来避免这种情况。guava 库中有一个很好/简单的实现。

开锁应用领域

如何通过钥匙获得锁

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

使用番石榴实现的示例代码:

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();
    }
}
于 2013-05-30T15:20:31.897 回答
1

Though I would personally prefer Guava's Striped<Lock> approach suggested by Keith, just for discussion & completeness, I'd like to point out that using a Dynamic Proxy, or the more generic AOP (Aspect Oriented Programming), is one approach.

So we would define an IStripedConcurrencyAware interface that would serve as the "something like a concurrency Decorator" that you desire, and the Dynamic Proxy / AOP method hijacking based on this interface would de-multiplex the method call into the appropriate Executor / Thread.

I personally dislike AOP (or most of Spring, for that matter) because it breaks the what-you-see-is-what-you-get simplicity of Core Java, but YMMV.

于 2014-03-09T18:39:20.583 回答