Reputation: 485
I am looking for a way to combine functionality from synchronized
with functionality from java.util.concurrent.locks.ReentrantReadWriteLock
. More specifically, I would like one method to lock only if the WriteLock
is set, but I want the lock to be on an object variable like synchronized
. Here are examples that will help explain:
ReentrantReadWriteLock Example
public void methodOne(String var) {
lock.writeLock().lock;
try {
// Processing
}
finally {
lock.writeLock().unlock();
}
}
public void methodTwo(String var) {
lock.readLock().lock;
try {
// Processing
}
finally {
lock.readLock().unlock();
}
}
In this example, methodTwo
can be invoked without blocking as long as the writeLock
on methodOne
has not been locked. This is good and the functionality I'm looking for, however when the writeLock
is locked, it is locked for all cases of var
. Instead, I would like for it only to lock on var
like with synchronized
:
Synchronized Example
public void methodOne(String var) {
synchronized(var) {
// Processing
}
}
public void methodTwo(String var) {
synchronized(var) {
// Processing
}
}
Now, the methods will only block conditional on the input. This is good and and what I'm looking for, however if there are multiple concurrent calls to methodTwo
with the same key, they will block. Instead, I would like this to be a "read" style lock and allow the call.
Essentially, I would like a ReadWriteLock that I can synchronize to a variable to obtain both sets of functionality.
Upvotes: 6
Views: 4265
Reputation:
If I got you correct I think the following will help
the solution is about just don't synchronize the object when you are going to read it, and vice versa for writing.
public void methodOne(String var) {
synchronized(var) {
// Processing the writting
}
}
public void methodTwo(String var) {
// Processing the reading
}
also I let me mention about the update method for a variable(if any)
if methodone
is going to update something as direct-modify(like a=12
, name="IBM"
), so we would omit the synchronized
block, and just mark the variable as volatile, but making a variable as volatile doesn't cover locking for read-modify updates (like a++
)
EDIT: Okay and if you are going to both having and eating your cake, the possible thing I think may help is synchronize on something else( than actual variable, think pthread
), and you would give some hand of a boolean
, okay the situation would be like this
private volatile boolean methodOneRunning=false;
private Object readLock=new Object(),writeLock=new Object();
public void methodOne(String var) {
synchronized(writeObject) {
methodOneRunning=true;
// Processing the eating
methodOneRunning=false;
}
}
public void methodTwo(String var) {
if(methodOneRunning){
synchronized(readLock){
//having the cake
}
}else{
//when thread one is not running!
}
}
and this is possible that method two start to reading at time 0
then thread one just finishes its job after method two entered at time 1
so here thread two will go reading the variable with locking..., so this may be solved by another thread which monitors the thread one activity and signal the thread two about the exit/termination state.
Upvotes: 0
Reputation: 6193
You could also use some kind of wrapper, if feasible. So will have a unique lock for each wrapped object.
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class ThreadSafeWrapper<T> {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final T object;
public ThreadSafeWrapper(T object) {
this.object = object;
}
public final void methodOneWriteLock(String var) {
lock.writeLock().lock();
try {
doMethodOne(object, var);
} finally {
lock.writeLock().unlock();
}
}
public final void methodTwoReadLock(String var) {
lock.readLock().lock();
try {
doMethodTwo(object, var);
} finally {
lock.readLock().unlock();
}
}
protected abstract void doMethodOne(T obj, String var);
protected abstract void doMethodTwo(T obj, String var);
}
Could be used like that:
public class SampleWrapper {
public static void main(String[] args) {
ThreadSafeWrapper<String> wrapper = new ThreadSafeWrapper<String>("lala") {
@Override
protected void doMethodOne(String obj, String var) {
System.out.println("method one: " + obj + " " + var);
}
@Override
protected void doMethodTwo(String obj, String var) {
System.out.println("method two: " + obj + " " + var);
}
};
wrapper.methodOneWriteLock("foo");
wrapper.methodTwoReadLock("bar");
}
}
You could do something similar with interfaces. Its just to point you in some direction.
Upvotes: 1
Reputation: 116878
Essentially, I would like a ReadWriteLock that I can synchronize to a variable to obtain both sets of functionality.
First off, depending on the number different values, this might create some huge number of locks. You might consider having some sort of classification or something so you can partition the updates to a small number instead. For example, is there a way you can subdivide the different var values. Maybe there are various types of values? If there is a small fixed number of values then this won't be a problem.
You are going to have to build some sort of collection of ReentrantReadWriteLock
. Maybe use a ConcurrentHashMap
which will store all of the locks.
Map<String, ReadWriteLock> lockMap =
new ConcurrentHashMap<String, ReadWriteLock>():
...
public void writeProcessing(String var) {
lock = lockMap.get(var);
lock.writeLock().lock;
try {
// Processing
} finally {
lock.writeLock().unlock();
}
}
You will need to register the different values beforehand. Or you can create them on demand. Again, this might create some huge number of locks which may be prohibitive.
public void writeProcessing(String var) {
lock = getLockForVar(var);
...
}
private ReadWriteLock getLockForVar(String var) {
ReadWriteLock lock = lockMap.get(var);
if (lock != null) {
return lock;
}
// this might create an extra lock because of race conditions...
lock = new ReadWriteLock();
ReadWriteLock current = lockMap.putIfAbsent(var, lock);
if (current == null) {
return lock;
} else {
return current;
}
}
Upvotes: 5