多线程Java线程自动唤醒
我有一个Java线程,它执行如下操作:
while (running) {
synchronized (lock) {
if (nextVal == null) {
try {
lock.wait();
} catch (InterruptedException ie) {
continue;
}
}
val = nextVal;
nextVal = null;
}
...do stuff with 'val'...
}
在其他地方,我设置如下值:
if (val == null) {
LOG.error("null value");
} else {
synchronized (lock) {
nextVal = newVal;
lock.notify();
}
}
偶尔(实际上每几百万次一次)nextVal会被设置为null。我已经输入了日志消息,我可以看到执行顺序如下所示:
- thread1将nextVal设置为newVal
- thread1调用lock。通知()
- 线程2从锁中唤醒。等等
- thread2将val设置为nextVal
- thread2将nextVal设置为null
- thread2与val有关
- thread2调用lock。等等
- 线程2从锁中唤醒。等等
- 没有其他线程调用lock。notify()和thread2未被中断
- thread2将val设置为nextVal(为null)
- 等等
我已经明确检查过了,锁第二次被唤醒,没有被中断
我做错什么了吗
# 1 楼答案
是的,
Thread
会自发地醒来。这一点在the Javadoc中明确说明:线程也可以在不被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒
您需要在循环中
wait
。javadoc中也明确提到了这一点:就你而言:
# 2 楼答案
这可能是虚假的唤醒,但这不是唯一可能的原因。这绝对是你逻辑上的问题。您需要将等待放在重新测试条件的循环中
当线程从等待中醒来时,它不再具有锁。它释放了锁当它开始等待时,它需要重新获得锁才能继续。由于线程亲和力(这可能是代码大部分时间工作的原因),刚刚醒来的线程通常是下一个线程,但仍然有可能不是;另一个线程可能会进入并阻塞锁,在被唤醒的线程获取锁之前,完成它的工作并将nextVal保留为null。这意味着线程在等待之前进行的null测试不再相关。一旦你有了锁,你必须回去再次测试
将代码更改为使用循环,如:
这样,测试是在线程拥有锁的情况下进行的,而while循环下面的块中发生的任何事情都可以确定nextVal实际上不是null
# 3 楼答案
虚假唤醒是相当常见的,因此总是建议在循环中的某个条件下
wait()
按如下方式更改代码:
特定于您共享的代码:
while
还可以帮助您避免在代码命中continue;
时释放和重新获取相同锁的不必要的开销参考文献:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#wait()