我希望能够基于某个ID来同步方法调用,类似于给定对象实例的并发装饰器。
例如:
所有调用参数为“id1”的方法的线程应该按顺序执行。
所有调用具有不同参数(例如“id2”)的方法的线程应该与调用参数为“id1”的方法的线程并行执行,但是再次按顺序执行彼此之间的线程。
因此,在我的想法中,可以通过每个方法参数拥有一个锁(http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html)实例来实现。每次使用该参数调用方法时,将查找对应于特定参数值(例如“id1”)的锁实例,并尝试获取锁的当前线程。
在代码中可以这样表示:
例如:
所有调用参数为“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;
}
}
那么你们认为呢?
STRIPED_LOCK
。我问这个问题是因为使用lazyWeakLock
意味着当没有强引用时可以回收锁(似乎与其锁定状态无关)。因此,我认为你的示例代码可能不是线程安全的。 - Trevor Freeman