hengxin hengxin - 2 months ago 6
Java Question

How to ensure that `methodB` is "blocked" if some threads are in `methodA` in Java?

Class clazz
has two methods
methodA()
and
methodB()
.


How to ensure that
methodB
is "blocked" if some threads are in
methodA
in Java (I am using Java 8)?


By "blocking methodB", I mean that "wait until no threads are in methodA()". (Thanks to @AndyTurner)

Note that the requirement above allows the following situations:


  1. Multiple threads are simultaneously in
    methodA
    .

  2. Multiple threads are in
    methodB
    while no threads are in
    methodA
    .

  3. Threads in
    methodB
    does not prevent other threads from entering
    methodA
    .






My trial: I use
StampedLock lock = new StampedLock
.


  • In
    methodA
    , call
    long stamp = lock.readLock()

  • Create a new method
    unlockB
    and call
    lock.unlockRead(stamp)
    in it.

  • In
    methodB
    , call
    long stamp = lock.writeLock()
    and
    lock.unlockWrite(stamp)
    .



However, this locking strategy disallows the second and the third situations above.




Edit: I realize that I have not clearly specified the requirements of the synchronization between
methodA
and
methodB
. The approach given by @JaroslawPawlak works for the current requirement (I accept it), but not for my original intention (maybe I should first clarify it and then post it in another thread).

Answer

I think this can do the trick:

private final Lock lock = new ReentrantLock();
private final Semaphore semaphore = new Semaphore(1);
private int threadsInA = 0;

public void methodA() {
    lock.lock();
    threadsInA++;
    semaphore.tryAcquire();
    lock.unlock();

    // your code

    lock.lock();
    threadsInA--;
    if (threadsInA == 0) {
        semaphore.release();
    }
    lock.unlock();
}

public void methodB() throws InterruptedException {
    semaphore.acquire();
    semaphore.release();

    // your code
}

Threads entering methodA increase the count and try to acquire a permit from semaphore (i.e. they take 1 permit if available, but if not available they just continue without a permit). When the last thread leaves methodA, the permit is returned. We cannot use AtomicInteger since changing the count and acquiring/releasing permit from semaphore must be atomic.

Threads entering methodB need to have a permit (and will wait for one if not available), but after they get it they return it immediately allowing others threads to enter methodB.

EDIT:

Another simpler version:

private final int MAX_THREADS = 1_000;
private final Semaphore semaphore = new Semaphore(MAX_THREADS);

public void methodA() throws InterruptedException {
    semaphore.acquire();

    // your code

    semaphore.release();
}

public void methodB() throws InterruptedException {
    semaphore.acquire(MAX_THREADS);
    semaphore.release(MAX_THREADS);

    // your code
}

Every thread in methodA holds a single permit which is released when the thread leaves methodA.

Threads entering methodB wait until all 1000 permits are available (i.e. no threads in methodA), but don't hold them, which allows other threads to enter both methods while methodB is still being executed.