Check for timeout in wait() loop

I want to check for a condition in a guarded wait() loop with a user-specified total timeout, similar to how LinkedBlockingQueue.poll() works.

For example collection can be appended to by different threads, but only when the collection contains a value equating to myItem within timeout, should the loop terminate successfully. If the user-specified timeout elapses, it should throw.

This is my current best approach:

long startTime = System.currentTimeMillis();
synchronized (collection) {
while (!collection.contains(myItem)) {
long remaining = System.currentTimeMillis() - startTime - timeout;
if (remaining < 0)
throw new TimeoutException();

collection.wait(remaining);
}
}


The problem here is that System.currentTimeMillis() (or System.nanoTime()) is called inside a synchronized context. IntelliJ's warning states:

While not necessarily representing a problem, such calls cause an expensive context switch, and are best kept out of synchronized contexts, if possible.

I wonder how much impact this 'expensive context switch' will have in practice, with Oracle JVM 8?

This is one alternative I came up with, but I don't like this as the actual timeout becomes ⌊timeout/10⌋*10 + delta. and the error will probably be much worse than the context switch.

synchronized (collection) {
int loops = 0;
while (!collection.contains(myItem)) {
if(loops++ > timeout / 10)
throw new TimeoutException();

collection.wait(10);
}
}


System.currentTimeMillis() is the system clock, and can change if someone changes the current date / time. It's not really a good way to accurately measure elapsed time. Use System.nanoTime() instead, which is invariant to system clock changes.

• This question must be from before I learned that fact, and since then I've been reinforcing it, on other SO questions too. Dec 9, 2019 at 10:23

I think you should be fine with System.currentTimeMillis(). Even though the method itself is not synchronized, mostly native method call internally would be synchronized.

Read this old but still useful article about the same thing.

http://www.devx.com/Java/Article/28685