Reputation: 90150
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:
Semaphore
.Semaphore.release()
in order to notify the reader.Semaphore.drainPermits()
, does some work, and blocks again on Semaphore.acquire
.What I don't like about the Semaphore
approach:
Integer.MAX_VALUE
. This is more of a theoretical problem than practical but still not ideal.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
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,
test()
that yields a boolean
result,mutex
that threads are required to own when performing the test or changing its result, andtrue
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
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