Spurious Wakeups in Java

What is Spurious Wakeups in Java threads?

Object.wait() is supposed to block until either another thread invokes the Object.notify() or Object.notifyAll() method, or a specified amount of time has elapsed. The various Condition.await() methods have similar behavior. However, it is possible for a thread to wake up without either of those occurring; these are called spurious wakeups.

Programs must check the condition predicate after the wait() method returns. A while loop is the best choice for checking the condition predicate both before and after invoking wait().Similarly, the await() method of the Condition interface also must be invoked inside a loop, According to the Java API [API 2014], Interface Condition.

When waiting upon a Condition, a “spurious wakeup” is permitted to occur, in general, as a concession to the underlying platform semantics. This has little practical impact on most application programs as a Condition should always be waited upon in a loop, testing the state predicate that is being waited for. An implementation is free to remove the possibility of spurious wakeups but it is recommended that applications programmers always assume that they can occur and so always wait in a loop.

Application Scenario
1 Wait until a condition becomes true

The incorrect code for this typically looks like:
Thread 1:

synchronized (this) {
  if (!condition) {
    wait();
  }
  doStuffAssumingConditionIsTrue();
}

Thread 2:

synchronized (this) {
  condition = true;
  notify();
}

If the call to wait() unblocks because of a spurious wakeup, then doStuffAssumingConditionIsTrue() will be called even though condition is still false. Instead of the if, you should use a while:
Thread 1:

synchronized (this) {
  while (!condition) {
    wait();
  }
  doStuffAssumingConditionIsTrue();
}

This ensures that you only proceed to doStuffAssumingConditionIsTrue() if condition is true. Note that the check of the condition variable must be inside the synchronized block; otherwise you will have a race condition between checking and setting the condition variable.

2 Wait until an event occurs

The incorrect code for this typically looks like:
Thread 1:

synchronized (this) {
  wait();
  doStuffAfterEvent();
}

Thread 2:

// when event occurs
synchronized (this) {
  notify();
}

If the call to wait() unblocks because of a spurious wakeup, then doStuffAfterEvent() will be called even though the event has not yet occurred. You should rewrite this code so that the occurrence of the event sets a condition variable as well as calls notify(), and the wait() is wrapped in a while loop checking the condition variable. That is, it should look just like the previous example.

3 Wait until either a condition becomes true or a timeout occurs

The incorrect code for this typically looks like:

synchronized (this) {
  if (!condition) {
    wait(timeout);
  }
  doStuffAssumingConditionIsTrueOrTimeoutHasOccurred();
}

A spurious wakeup could cause this to proceed to doStuffAssumingConditionIsTrueOrTimeoutHasOccurred() even if the condition is still false and time less than the timeout has elapsed. Instead, you should write:

synchronized (this) {
  long now = System.currentTimeMillis();
  long deadline = now + timeout;
  while (!condition && now < deadline) {
    wait(deadline - now);
    now = System.currentTimeMillis();
  }
  doStuffAssumingConditionIsTrueOrTimeoutHasOccurred();
}
3 Wait for a fixed amount of time

First, a warning: This type of waiting/sleeping is often done when the real intent is to wait until some operation completes, and then proceed. If that’s what you’re trying to do, please consider rewriting your code to use one of the patterns above. Otherwise you are depending on system-specific timing that will change when you run on different machines.

The incorrect code for this typically looks like:

synchronized (this) {
  // Give some time for the foos to bar
  wait(1000);
}

A spurious wakeup could cause this not to wait for a full 1000 ms. Instead, you should use Thread.sleep(), which is not subject to spurious wakeups:

Thread.sleep(1000);
3 Wait forever

The incorrect code for this typically looks like:

synchronized (this) {
  // wait forever
  wait();
}

A spurious wakeup could cause this not to wait forever. You should wrap the call to wait() in a while (true) loop:

synchronized (this) {
  // wait forever
  while (true) {
    wait();
  }
}

参考:
《WaitNotInLoop》
《THI03-J. Always invoke wait() and await() methods inside a loop》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值