How to differentiate when wait(long timeout) exit for notify or timeout?
Asked Answered
M

7

44

Having this wait declaration:

public final native void wait(long timeout) throws InterruptedException;

It could exit by InterruptedException, or by timeout, or because Notify/NotifyAll method was called in another thread, Exception is easy to catch but...

There is any way to know if the exits cause was timeout or notify?

EDIT:

This is a tricky way that could work, (although I don't like it)

          long tBefore=System.currentTimeMillis();
          wait(TIMEOUT);
          if ((System.currentTimeMillis() - tBefore) > TIMEOUT) 
            { 
               //timeout
            }
Mayfair answered 3/8, 2010 at 14:51 Comment(4)
I had a similar need and in my case it turned out that a Semaphore was better suited; Semaphore.tryAcquire(long timeout, TimeUnit unit) returns false if the timeout has elapsed.Isaisaac
Semaphore would work unless you need to have the ability to wait, which is not supported by the Semaphore public contract.Diatonic
need to change condition to (>=): if ((System.currentTimeMillis() - tBefore) >= TIMEOUT) Mallarme
Your solution is not correct. After a notify event, the scheduler resume the thread when other thread with higher priority are not running. This mean that, after the notify, an interval of time occurs before thread resuming, if it's enough to make true your condition, your code manage a timeout instead of the correct behaviour.Miscount
P
14

You can't differentiate between the two unless you provide some additional code. For example by adding a ThreadLocal Boolean that is set to true only on notify()

But first you must make sure your logic requires this differentiation.

Pyrometer answered 3/8, 2010 at 14:58 Comment(3)
The OP already knew an exception wouldn't be thrown and wants to distinguish between a timeout and a notify. This does not provide any useful information to that end.Kashgar
I do not see how it could help to set a thread-local boolean to true when doing notify(). The thread calling notify() will not be the same thread that wakes up from wait().Ive
You mean simple boolean field, I suppose. Not a ThreadLocal. But simple boolean field could help. It even doesn't need to be volatile, as wait/notify will be called from synchronized block anyway. Just set it to true before notify() and check/reset it after wait().Antonomasia
M
18

There is one more reason that notify can return: spurious wakeup. This is an unlikely but possible thing, because preventing spurious wakeups is very expensive on some hardware/OS combinations.

Because of this you always have to call wait() in a loop and re-check the condition that you are waiting for. During this work it's easy to check for timeout at the same time.

For details I recommend the book "Java Concurrency In Practice". And using higher level constructs that will get this all correct for you.

Moorland answered 3/8, 2010 at 15:11 Comment(0)
P
14

You can't differentiate between the two unless you provide some additional code. For example by adding a ThreadLocal Boolean that is set to true only on notify()

But first you must make sure your logic requires this differentiation.

Pyrometer answered 3/8, 2010 at 14:58 Comment(3)
The OP already knew an exception wouldn't be thrown and wants to distinguish between a timeout and a notify. This does not provide any useful information to that end.Kashgar
I do not see how it could help to set a thread-local boolean to true when doing notify(). The thread calling notify() will not be the same thread that wakes up from wait().Ive
You mean simple boolean field, I suppose. Not a ThreadLocal. But simple boolean field could help. It even doesn't need to be volatile, as wait/notify will be called from synchronized block anyway. Just set it to true before notify() and check/reset it after wait().Antonomasia
K
14

This doesn't exactly answer the question, but it will probably solve your problem: Use higher level concurrency mechanisms. Wait/notify is usually more low-level than you'd want, for this reason among many others.

For example, if you were using BlockingQueue.poll(long, TimeUnit), you could check if the result is null to know if you timed out.

Kashgar answered 3/8, 2010 at 15:1 Comment(0)
S
7

Don't use System.currentTimeMillis(), use System.nanoTime() instead.

The first one meassures absolute time (based on system clock) and might have curious results if the system time is changed. For example: A 5 second wait can have a duration of an hour if the clock is moved backward by an hour, or a 10 minute wait will be done after 0 seconds if the clock is moved foreward.

The second one meassures relative time. It will always run in one direction at constant speed, but it has no origin. That means that the values can only be used to meassure relative time, but can and should not be used to determine a date.

Seductive answered 16/12, 2014 at 14:5 Comment(1)
+1 for encouraging people to quit writing code that breaks on system clock updates. But you failed to mention that one should always check if the reason for waiting is still valid.Subcontraoctave
D
3

There's no way to tell directly - that is, you would have to add additional code to determine this. Often when you wait(), you're waiting for something to happen which changes the state of an object in some way - e.g. by setting a boolean variable, perhaps. If that's the case, then you may be able to simply check the state of that variable to see if the event occurred, or you merely timed out. Or you can look at the value of System.currentTimeMillis() to see i the elapsed time is greater than or equal to the timeout period - if it is, that would be a clue you have probably timed out (though it's not an absolute guarantee). Or if the elapsed time is less than the timeout period then you certainly have not timed out. Does that help?

Durable answered 3/8, 2010 at 15:22 Comment(1)
@Hernán Eche As I have put in my answer its not an absolute guarantee. if you can tell , why you want to identify whether it timedout ? We can look for some solution.Durable
C
3

You should use not wait/notify approach.

Will be better to use Lock with Condidions https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html#await-long-java.util.concurrent.TimeUnit-

It has await with timeout and will return false if the waiting time detectably elapsed before return from the method, else true

Curate answered 23/7, 2018 at 8:40 Comment(0)
M
1

Exception is not thrown on notify and time out.

I think it's better to rely on java.lang.concurrent package synchronisation objects instead of using Object.wait().

Mesosphere answered 3/8, 2010 at 14:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.