107

I'm writing a listener thread for a server, and at the moment I'm using:

while (true){
    try {
        if (condition){
            //do something
            condition=false;
        }
        sleep(1000);

    } catch (InterruptedException ex){
        Logger.getLogger(server.class.getName()).log(Level.SEVERE, null, ex);
    }
}

With the code above, I'm running into issues with the run function eating all the cpu time looping. The sleep function works, but it seems be a makeshift fix, not a solution.

Is there some function which would block until the variable 'condition' became 'true'? Or is continual looping the standard method of waiting until a variable's value changes?

Alex Weitz
  • 3,199
  • 4
  • 34
  • 57
Rolan
  • 2,924
  • 7
  • 34
  • 46
  • 4
    Why would the code above eat up all your cpu, it seems like it will only launch once a second. Anyways see this thread: http://stackoverflow.com/questions/289434/how-to-make-a-java-thread-wait-for-another-threads-output – jontro May 14 '11 at 00:53
  • 3
    For complete coverage of this subject, see Chapter 14 of _Java Concurrency in Practice_. But more generally, you probably want to use higher-level utilities like `BlockingQueue`, `Semaphore`, or `CountDownLatch` rather than the low-level mechanisms. – Brian Goetz Oct 12 '16 at 16:56

7 Answers7

82

Polling like this is definitely the least preferred solution.

I assume that you have another thread that will do something to make the condition true. There are several ways to synchronize threads. The easiest one in your case would be a notification via an Object:

Main thread:

synchronized(syncObject) {
    try {
        // Calling wait() will block this thread until another thread
        // calls notify() on the object.
        syncObject.wait();
    } catch (InterruptedException e) {
        // Happens if someone interrupts your thread.
    }
}

Other thread:

// Do something
// If the condition is true, do the following:
synchronized(syncObject) {
    syncObject.notify();
}

syncObject itself can be a simple Object.

There are many other ways of inter-thread communication, but which one to use depends on what precisely you're doing.

Neuron
  • 5,141
  • 5
  • 38
  • 59
EboMike
  • 76,846
  • 14
  • 164
  • 167
  • 3
    You're very welcome! Keep in mind there are other ways to synchronize, like semaphores, blocking queues, etc... it all depends on what you want to do. Objects are great general-purpose thread synchronization tools. Good luck with your app! – EboMike May 14 '11 at 04:45
  • 14
    The try catch should be wrapped in a loop testing the real underlying condition to guard against a spurious wake up (see the wait doco). – Lawrence Dol May 14 '11 at 06:44
  • @Software Monkey: Absolutely, I omitted that for brevity, but you're right, I should have mentioned that - obviously, if the wait() got interrupted, it the condition most likely hasn't been met yet, so the execution should go back to the wait. – EboMike May 14 '11 at 06:48
  • 12
    Its worth noting if notifyAll is called first, wait() will wait forever, even though the condition was met before it started waiting. – Peter Lawrey May 14 '11 at 08:29
  • 4
    This answer is quite dated since java.concurent has come out. The cleaner and less mistake prone way to wait is to use CountDownLatch per Effective Java Ed 2 – Alan Berezin May 19 '16 at 01:57
  • 2
    @PeterLawrey It should also be noted (even more than eight years after this answer was given) that using `notfify` instead of `notifyAll` can lead to funny effects if another thread starts waiting this way as well, because `notify` only notified one of the waiting threads (assume that it's random). – Lothar Oct 27 '19 at 11:58
76

EboMike's answer and Toby's answer are both on the right track, but they both contain a fatal flaw. The flaw is called lost notification.

The problem is, if a thread calls foo.notify(), it will not do anything at all unless some other thread is already sleeping in a foo.wait() call. The object, foo, does not remember that it was notified.

There's a reason why you aren't allowed to call foo.wait() or foo.notify() unless the thread is synchronized on foo. It's because the only way to avoid lost notification is to protect the condition with a mutex. When it's done right, it looks like this:

Consumer thread:

try {
    synchronized(foo) {
        while(! conditionIsTrue()) {
            foo.wait();
        }
        doSomethingThatRequiresConditionToBeTrue();
    }
} catch (InterruptedException e) {
    handleInterruption();
}

Producer thread:

synchronized(foo) {
    doSomethingThatMakesConditionTrue();
    foo.notify();
}

The code that changes the condition and the code that checks the condition is all synchronized on the same object, and the consumer thread explicitly tests the condition before it waits. There is no way for the consumer to miss the notification and end up stuck forever in a wait() call when the condition is already true.

Also note that the wait() is in a loop. That's because, in the general case, by the time the consumer re-acquires the foo lock and wakes up, some other thread might have made the condition false again. Even if that's not possible in your program, what is possible, in some operating systems, is for foo.wait() to return even when foo.notify() has not been called. That's called a spurious wakeup, and it is allowed to happen because it makes wait/notify easier to implement on certain operating systems.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • 1
    Should we place the try-catch within or outside the while loop? Which way is recommended and why ? – Jaydev May 16 '16 at 02:08
  • 3
    @JaydevKalivarapu, Assuming you're asking about the `InterruptedException`, Right? It's up to you to decide what an interrupt means, but in _most_ cases, it probably means "stop waiting" and do something else (like, for example, shut down the whole program.) So in most cases, you'll want it to look like my example above, with the interrupt handler outside the loop. – Solomon Slow May 16 '16 at 02:20
  • 7
    @JaydevKalivarapu, P.S.: Back when I wrote the above answer, I was not aware that that pattern has a name: The Oracle Java tutorials call it a _guarded block_. You can read about it at, https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html – Solomon Slow May 16 '16 at 02:22
  • What if I put the `while(!conditionIsTrue())` loop outside the `synchronized` block? Will this be broken? – Jian Guo Oct 08 '19 at 08:20
  • 1
    @JianGuo, `foo.wait()` will throw `IllegalMonitorStateException` if `foo` is not locked. That's meant to remind you that it does not make sense to `wait()` in code that does not hold the lock. My answer, above, touches on the reason why, but if you want a thorough explanation then you should read the tutorial. https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html – Solomon Slow Oct 08 '19 at 12:41
  • @SolomonSlow Sry for my previous question here as I have made a bad description here. My question is : ``` try { while(! conditionIsTrue()) { synchronized(foo) { foo.wait(); } } doSomethingThatRequiresConditionToBeTrue(); } } catch (InterruptedException e) { handleInterruption(); } ``` Will this be broken? – Jian Guo Oct 09 '19 at 03:32
  • 1
    @JianGuo, If you do that, then what could happen is; (1) The consumer tests the condition and finds it to be false, (2) The consumer attempts to enter `synchronized(foo)` but it is blocked because the producer already is synchronized on `foo`, (3) The producer cause the condition to become true, calls `foo.notify()`, and then releases the lock, (4) The consumer enters the `synchronized(foo)` block, and calls `foo.wait()`. Now the consumer is stuck waiting for a notification that never will arrive. This problem sometimes is called, "lost notification". – Solomon Slow Oct 09 '19 at 12:42
48

As nobody published a solution with CountDownLatch. What about:

public class Lockeable {
    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    public void doAfterEvent(){
        countDownLatch.await();
        doSomething();
    }

    public void reportDetonatingEvent(){
        countDownLatch.countDown();
    }
}
Neuron
  • 5,141
  • 5
  • 38
  • 59
borjab
  • 11,149
  • 6
  • 71
  • 98
  • 4
    The disadvantage of CountDownLatch is that it's not reusable: once the count become zero it is no longer usable – Mohdroid Oct 08 '21 at 09:06
26

Similar to EboMike's answer you can use a mechanism similar to wait/notify/notifyAll but geared up for using a Lock.

For example,

public void doSomething() throws InterruptedException {
    lock.lock();
    try {
        condition.await(); // releases lock and waits until doSomethingElse is called
    } finally {
        lock.unlock();
    }
}

public void doSomethingElse() {
    lock.lock();
    try {
        condition.signal();
    } finally {
        lock.unlock();
    }
}

Where you'll wait for some condition which is notified by another thread (in this case calling doSomethingElse), at that point, the first thread will continue...

Using Locks over intrinsic synchronisation has lots of advantages but I just prefer having an explicit Condition object to represent the condition (you can have more than one which is a nice touch for things like producer-consumer).

Also, I can't help but notice how you deal with the interrupted exception in your example. You probably shouldn't consume the exception like this, instead reset the interrupt status flag using Thread.currentThread().interrupt.

This because if the exception is thrown, the interrupt status flag will have been reset (it's saying "I no longer remember being interrupted, I won't be able to tell anyone else that I have been if they ask") and another process may rely on this question. The example being that something else has implemented an interruption policy based on this... phew. A further example might be that your interruption policy, rather that while(true) might have been implemented as while(!Thread.currentThread().isInterrupted() (which will also make your code be more... socially considerate).

So, in summary, using Condition is rougly equivalent to using wait/notify/notifyAll when you want to use a Lock, logging is evil and swallowing InterruptedException is naughty ;)

RubioRic
  • 2,442
  • 4
  • 28
  • 35
Toby
  • 9,523
  • 8
  • 36
  • 59
  • 3
    Using `Condition` + `Lock` is **not** equivalent to the `Object` synchronization methods + `synchronized`. The former allow notifications before the condition is awaited -- on the other hand, if you call `Object.notify()` before `Object.wait()`, the thread will block forever. Furthermore, `await()` must be called in a loop, see docs. – TheOperator Mar 14 '16 at 12:14
  • @TheOperator Regarding "The former allow notifications before the condition is awaited" - I looked through the Javadoc for [`Condition`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html) but couldn't find text to support this claim. Can you please explain your statement? – Nayuki Feb 21 '17 at 07:51
  • 1
    The example code is wrong, await needs to be called in a loop. See the API doc for Condition. – Nathan Hughes Apr 16 '17 at 04:55
  • @TheOperator `Object.wait()` must also be called in a loop. – Sassa NF Jan 08 '21 at 16:11
8

You could use a semaphore.

While the condition is not met, another thread acquires the semaphore.
Your thread would try to acquire it with acquireUninterruptibly()
or tryAcquire(int permits, long timeout, TimeUnit unit) and would be blocked.

When the condition is met, the semaphore is also released and your thread would acquire it.

You could also try using a SynchronousQueue or a CountDownLatch.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
5

One could also leverage CompletableFutures (since Java 8):

final CompletableFuture<String> question = new CompletableFuture<>();

// from within the consumer thread:
final String answer = question.get(); // or: event.get(7500000, TimeUnit.YEARS)

// from within the producer thread:
question.complete("42");
rdesgroppes
  • 988
  • 12
  • 11
4

Lock-free solution(?)

I had the same issue, but I wanted a solution that didn't use locks.

Problem: I have at most one thread consuming from a queue. Multiple producer threads are constantly inserting into the queue and need to notify the consumer if it's waiting. The queue is lock-free so using locks for notification causes unnecessary blocking in producer threads. Each producer thread needs to acquire the lock before it can notify the waiting consumer. I believe I came up with a lock-free solution using LockSupport and AtomicReferenceFieldUpdater. If a lock-free barrier exists within the JDK, I couldn't find it. Both CyclicBarrier and CoundDownLatch use locks internally from what I could find.

This is my slightly abbreviated code. Just to be clear, this code will only allow one thread to wait at a time. It could be modified to allow for multiple awaiters/consumers by using some type of atomic collection to store multiple owner (a ConcurrentMap may work).

I have used this code and it seems to work. I have not tested it extensively. I suggest you read the documentation for LockSupport before use.

/* I release this code into the public domain.
 * http://unlicense.org/UNLICENSE
 */

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;

/**
 * A simple barrier for awaiting a signal.
 * Only one thread at a time may await the signal.
 */
public class SignalBarrier {
    /**
     * The Thread that is currently awaiting the signal.
     * !!! Don't call this directly !!!
     */
    @SuppressWarnings("unused")
    private volatile Thread _owner;

    /** Used to update the owner atomically */
    private static final AtomicReferenceFieldUpdater<SignalBarrier, Thread> ownerAccess =
        AtomicReferenceFieldUpdater.newUpdater(SignalBarrier.class, Thread.class, "_owner");

    /** Create a new SignalBarrier without an owner. */
    public SignalBarrier() {
        _owner = null;
    }

    /**
     * Signal the owner that the barrier is ready.
     * This has no effect if the SignalBarrer is unowned.
     */
    public void signal() {
        // Remove the current owner of this barrier.
        Thread t = ownerAccess.getAndSet(this, null);

        // If the owner wasn't null, unpark it.
        if (t != null) {
            LockSupport.unpark(t);
        }
    }

    /**
     * Claim the SignalBarrier and block until signaled.
     *
     * @throws IllegalStateException If the SignalBarrier already has an owner.
     * @throws InterruptedException If the thread is interrupted while waiting.
     */
    public void await() throws InterruptedException {
        // Get the thread that would like to await the signal.
        Thread t = Thread.currentThread();

        // If a thread is attempting to await, the current owner should be null.
        if (!ownerAccess.compareAndSet(this, null, t)) {
            throw new IllegalStateException("A second thread tried to acquire a signal barrier that is already owned.");
        }

        // The current thread has taken ownership of this barrier.
        // Park the current thread until the signal. Record this
        // signal barrier as the 'blocker'.
        LockSupport.park(this);
        // If a thread has called #signal() the owner should already be null.
        // However the documentation for LockSupport.unpark makes it clear that
        // threads can wake up for absolutely no reason. Do a compare and set
        // to make sure we don't wipe out a new owner, keeping in mind that only
        // thread should be awaiting at any given moment!
        ownerAccess.compareAndSet(this, t, null);

        // Check to see if we've been unparked because of a thread interrupt.
        if (t.isInterrupted())
            throw new InterruptedException();
    }

    /**
     * Claim the SignalBarrier and block until signaled or the timeout expires.
     *
     * @throws IllegalStateException If the SignalBarrier already has an owner.
     * @throws InterruptedException If the thread is interrupted while waiting.
     *
     * @param timeout The timeout duration in nanoseconds.
     * @return The timeout minus the number of nanoseconds that passed while waiting.
     */
    public long awaitNanos(long timeout) throws InterruptedException {
        if (timeout <= 0)
            return 0;
        // Get the thread that would like to await the signal.
        Thread t = Thread.currentThread();

        // If a thread is attempting to await, the current owner should be null.
        if (!ownerAccess.compareAndSet(this, null, t)) {
            throw new IllegalStateException("A second thread tried to acquire a signal barrier is already owned.");
        }

        // The current thread owns this barrier.
        // Park the current thread until the signal. Record this
        // signal barrier as the 'blocker'.
        // Time the park.
        long start = System.nanoTime();
        LockSupport.parkNanos(this, timeout);
        ownerAccess.compareAndSet(this, t, null);
        long stop = System.nanoTime();

        // Check to see if we've been unparked because of a thread interrupt.
        if (t.isInterrupted())
            throw new InterruptedException();

        // Return the number of nanoseconds left in the timeout after what we
        // just waited.
        return Math.max(timeout - stop + start, 0L);
    }
}

To give a vague example of usage, I'll adopt james large's example:

SignalBarrier barrier = new SignalBarrier();

Consumer thread (singular, not plural!):

try {
    while(!conditionIsTrue()) {
        barrier.await();
    }
    doSomethingThatRequiresConditionToBeTrue();
} catch (InterruptedException e) {
    handleInterruption();
}

Producer thread(s):

doSomethingThatMakesConditionTrue();
barrier.signal();
axblount
  • 2,639
  • 23
  • 27