Random42
Random42

Reputation: 9159

Multiple threads calling a method while blocking if another method is called from another thread

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

Answers (2)

Solomon Slow
Solomon Slow

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

bowmore
bowmore

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

Related Questions