Reputation: 9159
Suppose there are two methods methodA()
and methodB()
that are called from distinct threads. What I want is when methodA()
is called then the thread that called methodB()
be blocked until methodA()
is finished, and the other way round. This can be easily achieved with a semaphore:
private Semaphore semaphore = new Semaphore(1, true);
public void methodA() {//ignored try catch finally
semaphore.acquire();
//do stuff
semaphore.release();
}
public void methodB() {
semaphore.acquire();
//do stuff
semaphore.release();
}
But the thing is that I want multiple threads executing concurrently methodA()
or methodB()
. Multiple threads should be able to execute at the same time methodA()
so long no one executes methodB()
; the above solution does not work in this case because if a thread executes methodA()
no other thread can execute methodA()
even if there is no thread executing methodB()
.
Any other solution I can think of either needs synchronization in methods or does something else that does not allow multiple threads executing a single method if no threads execute the other one.
Upvotes: 0
Views: 452
Reputation: 27115
Sounds like you want a reader/writer lock (e.g., java.util.concurrent.locks.ReentrantReadWriteLock). It allows any number of 'readers' to use the resource at the same --OR-- it allows exactly one 'writer' to use it, but not both.
A good implementation, like the Java version, will be slightly complicated by the need to provide 'fairness.' E.g., it must not 'starve' a writer that wants the resource even when there are so many readers that there is never a moment when the resource is idle.
Upvotes: 0
Reputation: 11280
This is a situation very much like a trafficlight at a crossroads. Either road A gets to pass or road B, but never both simultaneously.
So let's say we have a TrafficLight
class that acts a little like a Semaphore
that monitors two roads "A" and "B". A thread wanting to pass "A" can ask permission for "A" and shall be granted that permission, or block if a "B" permission has not been released, and vice versa.
In the past I have made such a TrafficLight
class as an exercise (which can actually monitor more than 2 states) :
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TrafficLight<T> {
private ReentrantLock lock = new ReentrantLock(true);
private Condition switched = lock.newCondition();
private int registered;
private int maxBeforeYield = 20;
private T state;
public void acquire(T t) throws InterruptedException {
lock.lock();
try {
while ((state != null && !state.equals(t)) || maxBeforeYield == 0) {
switched.await();
}
if (state == null) {
state = t;
}
registered++;
maxBeforeYield--;
} finally {
lock.unlock();
}
}
public void release() {
lock.lock();
try {
registered--;
if (registered == 0) {
state = null;
maxBeforeYield = 20;
switched.signalAll();
}
} finally {
lock.unlock();
}
}
}
Note the maxBeforeYield
: one problem the TrafficLight may encounter is that so a flood of requests for "A" is so great, that requests for "B" never get a chance. Therefore the TrafficLight counts requests, and after a number of requests (maxBeforeYield
) it starts blocking "A" requests as well, then nicely waits until all permissions have been returned, and then gives another state a chance, the ReentrantLock
needs to be fair, to make sure the 'direction' switches unless only requests for the same direction are blocked.
Given this TrafficLight
class you could change your code into :
private TrafficLight<String> trafficLight = new TrafficLight<>();
public void methodA() {//ignored try catch finally
trafficLight.acquire("A");
//do stuff
trafficLight.release();
}
public void methodB() {
trafficLight.acquire("B");
//do stuff
trafficLight.release();
}
Upvotes: 1