I want to synchronize method calls on basis some id i.e. something like a concurrency Decorator of a given object instance.
For example:
All threads which call the method with param "id1", should execute serially to one another.
All of the rest, which call the method with different argument, say "id2", should execute in parallel to the threads which call the method with param "id1", but again serially to each other.
So in my mind this can be implemented by having a lock (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html) instance per such method param. Each time the method is called with the param, the lock instance corresponding to the specific param value (e.g. "id1") would be looked up and the current thread would try to obtain the lock.
Speaking in code:
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();
}
It seems that this works - I did some performance testing with jmeter and so on. Still, as we all know concurrency in Java is a tricky thing, I decided to ask for your opinion here.
I can't stop thinking that there could be a better way to accomplish this. For example by using one of the BlockingQueue implementations. What do you think?
I also can't really decide for sure if there is a potential synchronization problem with getting the lock i.e. the protected Lock getLock(String id)
method. I am using a synchronized collection, but is that enough? I.e. shouldn't it be something like the following instead of what I currently have:
protected Lock getLock(String id) {
synchronized(concurrentPolicyMap) {
Lock lock = concurrentPolicyMap.get(id);
if (lock == null) {
lock = createLock();
concurrentPolicyMap.put(id, lock);
}
return lock;
}
}
So what do you guys think?
When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.
When a method is declared as synchronized; the thread holds the monitor or lock object for that method's object. If another thread is executing the synchronized method, your thread is blocked until that thread releases the monitor.
The JVM uses locks in conjunction with monitors. A monitor is basically a guardian in that it watches over a sequence of code, making sure only one thread at a time executes the code. Each monitor is associated with an object reference.
ReadWriteLock. The interface ReadWriteLock specifies another type of lock maintaining a pair of locks for read and write access.
Lock creation issues aside, the pattern is OK except that you may have an unbounded number of locks. Generally people avoid this by creating/using a Striped lock. There is a good/simple implementation in the guava library.
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
Example code using guava implementation:
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With