I have below class structures in my project for class A and class B:
class A {
private String id;
private List<B> bs = new ArrayList<B>();
A(String id){
this.id = id;
}
public List<B> getBs(){
return bs;
}
public void setBs(List<B> bs){
this.bs=bs;
}
public void addBs(List<B> bs){
this.bs.addAll(bs);
}
public void addB(B b){
this.bs.add(b);
}
}
class B {
private String id;
private List<String> tags;
B(String id){
this.id = id;
}
public List<String> getTags(){
return tags;
}
public void setTags(List<String> tags){
this.tags = tags;
}
public void addTags(List<String> tags){
this.tags.addAll(tags);
}
public void addTag(String tag){
this.tags.add(tag);
}
}
Also we have a cache class:
class CacheService {
private static final ConcurrentHashMap<String, Object> CACHE = new ConcurrentHashMap<String, Object>();
public static Object get(String id){
return CACHE.get(id);
}
public static void put(String id, Object obj){
return CACHE.put(id, obj);
}
}
Now objects for class A and B are created using unique IDs and put in this cache by <id, object> combination. For example:
A a1 = new A("10");
CacheService.put("10", a1);
A a2 = new A("11");
CacheService.put("11", a2);
B b1 = new B("1");
CacheService.put("1", b1);
B b2 = new B("2");
CacheService.put("2", b2);
B b3 = new B("3");
CacheService.put("3", b3);
B b4 = new B("4");
CacheService.put("4", b4);
Also I am putting class B objects in the List<B>
inside objects a1
and a2
. It is important to note that a unique B object is only put once in any A object:
a1.add(b1);
a1.add(b2);
a2.add(b3);
a2.add(b4);
This way we can have multiple objects for class A and B in the CACHE.
Scenario: Now multiple threads access this CACHE but some of them end up to get class A objects and others class B objects depending upon ID specified by user. These threads actually want to read or update information on these objects.
Question: My requirement is when a thread has accessed an object of class A (for example a1
) to update it then no other thread should be able to read or update a1
as well as all class B objects (b1
and b2
in this case) which are added to the List<B>
inside object a1
until I am finished with all updates on a1
. Please tell me how can I acquire a lock in this scenario?
To have deep copying I suggest you to have a Lock
in each instance of A and B and have them both implement some interface with lock()
and unlock()
methods, where class A
will acquire its own lock, and all locks of B's
. Then, lock an object before using it, and unlock after you're done.
EDIT: So your course of action would be:
Lockable
with two methods: lock()
and unlock()
Lockable
instead of Object
add a private field to both A and B
private final Lock lock = new ReentrantLock();
Lockable
in B will be just to call same methods on the locklock()
will acquire local instance of lock, and iterate the list of b's and call their lock()
method as well. Same for unlock()
lock()
on that object and then unlock()
when you're done.The synchronized
keyword will help you with this. You can either declare an entire method as synchronized
or you can have synchronized()
blocks, which lock on a specific key object which you define. When a synchronized()
block is entered, no other blocks which lock with that same key object can be accessed by another thread until the block is exited.
See the Java tutorial on synchronization here.
For your example, you could do either of the following:
public synchronized void addB(B b) {
this.bs.add(b);
}
OR
... declare a lock object in your class ...
private final Object LOCK = new Object();
... and use it a synchronized()
block:
public void addB(B b) {
synchronized(LOCK) {
this.bs.add(b);
}
}
The advantages to using the second over the first is that you can completely, explicitly control which sections of code are locked (rather than just the entire method call). When dealing with concurrency, you want to synchronize as little as possible for efficiency purposes, so using this you can perform synchronization on only the bare minimum.
Also, I'm sure someone will point out that you don't need to explicitly declare another lock object, because you are able to synchronize on the current object using the this
keyword. However, this other StackOverflow question and answer sum up the reasons I would not recommend doing so nicely.
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