I have two classes each with one method.
class A {
private void insert(String usedId){
// ...
}
}
class B {
private void refresh(String userId){
// ...
}
}
Each method is called from a different Thread
. They are called from different Thread
s for different userId
s.
I need to lock the first method when second is called and vise versa for the same userId
.
Is the best choice to hold a List
of ids and set the lock?
We introduce a LockDispenser
. You pass this object to all A
s and B
s you want to have thread safe. It will provide Lock
objects with createLock(String forId)
which need to be released after use by calling releaseLock(String forId)
.
public class LockDispenser {
private final Map<String, Lock> dispenser = new LinkedHashMap<>();
public Object createLock(String forId) {
synchronized (dispenser) {
if (!dispenser.containsKey(forId)) {
dispenser.put(forId, new Lock());
}
Lock lock = dispenser.get(forId);
lock.referenceCounter++;
return lock;
}
}
public void releaseLock(String forId) {
synchronized (dispenser) {
Lock lock = dispenser.get(forId);
lock.referenceCounter--;
if (lock.referenceCounter == 0) {
dispenser.remove(forId);
}
}
}
public static class Lock {
private int referenceCounter = 0;
}
}
Now the actual thread safety comes from using the Lock
in a synchronized
block.
public class A {
private LockDispenser dispenser;
public A(LockDispenser dispenser) {
this.dispenser = dispenser;
}
private void insert(String userId) {
synchronized (dispenser.createLock(userId)) {
// code
}
dispenser.releaseLock(userId); // consider putting this in a finally block
}
}
public class B {
private LockDispenser dispenser;
public B(LockDispenser dispenser) {
this.dispenser = dispenser;
}
private void refresh(String userId) {
synchronized (dispenser.createLock(userId)) {
// code
}
dispenser.releaseLock(userId); // consider putting this in a finally block
}
}
Make sure releaseLock(String forId)
is called even if an Exception is thrown. You can do this by putting it into a finally
block.
And create them like such:
public static void main(String... args) {
LockDispenser fooLock = new LockDispenser();
A fooA = new A(fooLock);
B fooB = new B(fooLock);
LockDispenser barLock = new LockDispenser();
A barA = new A(barLock);
B barB = new B(barLock);
}
fooA
and fooB
are thread safe with each other and so are barA
and barB
.