Gili
Gili

Reputation: 90150

Is it possible to block a thread until AtomicBoolean contains a desired value?

I've got a system with many writers and a single reader, each running in a separate thread. The writers notify the reader when work is available, and the reader blocks until it is notified.

Given the number of writers, I want to use a lock-free implementation to notify the reader. Every time the reader wakes up, it resets the notification flag, does some work, and blocks waiting for more notifications to arrive. Essentially I'm looking for the equivalent of an AtomicBoolean with an ability to block until its value becomes true.

What I've tried so far:

What I don't like about the Semaphore approach:

Is there a data structure that is equivalent to AtomicBoolean with an ability to block waiting on a particular value?

Alternatively, is there a thread-safe manner to ensure that Semaphore's number of permits never surpass a certain value?

Upvotes: 3

Views: 359

Answers (2)

Solomon Slow
Solomon Slow

Reputation: 27190

FYI: The Java language includes a general mechanism for threads to await arbitrary events caused by other threads. It's rather primitive, and in many applications, you'd be better off using some higher-level, problem-specific tool such as BlockingQueue, CompletableFuture, CountdownLatch, etc. But, for those problems where the higher-level classes all "feel a bit heavy-handed," the Object class has object.wait(), object.notify(), and object.notifyAll().*

The idea is,

  • You have some test() that yields a boolean result,
  • There is a mutex that threads are required to own when performing the test or changing its result, and
  • There is at least one thread that needs to wait until the test yields true before it can proceed.
final Object mutex = new Object();
public boolean test() { ... }
public boolean procedureThatAffectsTheTestResult() { ... }
public boolean procedureThatRequiresTestResultToBeTrue() { ... }

A thread that needs to wait until the test result is true can do this:

synchronized (mutex) {
    while (! test()) {
        try {
            mutex.wait();
        }
        catch (InterruptedException ex) {
            ...use your shameful imagination here...
        }
    }
    procedureThatRequiresTestResultToBeTrue();
}

Any thread that can change the test result should do it like so:

synchronized (mutex) {
    procedureThatAffectsTheTestResult();
    mutex.notifyAll();   //or, mutex.notify() *IF* you know what you are doing.
}

The mutex.notifyAll() call will wake up every thread that happens to be sleeping in a mutex.wait() call at that same moment. mutex.notify() is trickier, but it will improve the performance of some applications because it will arbitrarily choose just one thread if more than one is waiting.

You may be wondering how a thread could ever enter a synchronized (mutex) block to change the test() result when another thread already is wait()ing inside another synchronized (mutex) block. The secret is that mutex.wait() temporarily unlocks the mutex while it is waiting, and then it guarantees to re-lock the mutex before returning or throwing an exception.

For a more complete description of when and why and how to use this feature, see the tutorial: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html


* You can also do practically the same thing using a Condition object and a ReentrantLock, but that's a topic for another day.

Upvotes: 1

Louis Wasserman
Louis Wasserman

Reputation: 198391

BlockingQueue<Singleton> would do this adequately.

You would create, for example, an ArrayBlockingQueue<Singleton>(1), and then your waiter would look like:

queue.take();

… and the notifier would look like:

queue.offer(Singleton.INSTANCE)

… with the use of offer ensuring that multiple releases are combined together.

Upvotes: 1

Related Questions