Reputation: 4593
I am playing around with Conditions in ReentrantLock in the context of a resource pool, from what I can see it simplifies thread communications. My questions is, I end up organically writing strange Conditionals such as acquiredMapEmpty, freeQueueNotEmpty, change that await and single different things. Technically they can be all replaced by one Conditional or be broken up into more Conditionals -- is there a rule of rule of thumb for:
Here is example of removing a resource.
public boolean remove(R resource) throws InterruptedException {
System.out.println("Remove, resource: " + resource + " with thread: " + Thread.currentThread().getName());
if (resource == null) {
throw new NullPointerException();
}
mainLock.lock();
try {
if (!isOpen) {
throw new IllegalStateException("Not open");
}
Object lock = locks.get(resource);
if (lock == null) {
return false;
}
if (freeQueue.remove(resource)) {
locks.remove(resource);
if (!freeQueue.isEmpty()) {
freeQueueNotEmpty.signalAll();
}
return true;
}
while (!freeQueue.contains(resource)) {
change.await();
if (!isOpen) {
throw new IllegalStateException("Not open");
}
lock = locks.get(resource);
if (lock == null) {
return false;
}
}
if (freeQueue.remove(resource)) {
locks.remove(resource);
if (!freeQueue.isEmpty()) {
freeQueueNotEmpty.signalAll();
}
return true;
}
return false;
} finally {
mainLock.unlock();
}
}
Upvotes: 0
Views: 737
Reputation: 16898
Well, as a rule of thumb, I tend to have as many condition variables as there are reasons for a thread to block. The rationale is that when you signal a condition variable you want to wake up a thread that is waiting for that specific change in the state you're signalling and you really, really want to avoid the "thundering herd" syndrome - waking up all the threads, blocked on a condition, only to have one of them make progress and all the others going back to sleep, having wasted precious CPU time and thrashed caches in the meantime.
Upvotes: 1
Reputation: 4137
In my humble opinion, there is no thumb rule here.
It really depends on use-cases, and synchronization is not an easy topic at all.
Of course you should not "exhaust" your system with locks - locks are an expensive resource.
If you feel you need to coordinate threads, and to protected shared resources, then you have no choice but to use synchronization objects.
Each time you use a synch object such as a lock or a condition that is obtained from a lock, you should ask yourself what is the use-case, do you really need the lock, what other threads need to be coordinated (what are their flows).
I want to take this question a bit off-topic and give you an example - in which I discovered that we have several threads using synchronized keyword,
but some perform read, and some write, so I switched to ReaderWriterLock -
so should be your case,
don't use all kinds of synch-objects just cause they are cool - carefully understand if and where they are really needed.
Upvotes: 1