Java 虚假唤醒怎么网上都没有一个好的例子?

2017-03-01 19:45:53 +08:00
 esolve

到底 java 虚假唤醒是怎么回事? 网上都没有一个好的例子啊

3409 次点击
所在节点    Java
7 条回复
kaneg
2017-03-01 19:47:23 +08:00
你到底在说啥?请用专业术语
snnn
2017-03-01 20:05:23 +08:00
听不懂
rogerchen
2017-03-01 21:33:49 +08:00
@kaneg
@snnn
说的是 Spurious wakeup

@esolve
就是 cv 返回了也不一定可以拿 mutex ,还是要检测。这是多线程编程基础,跟语言关系不大, java 例子那本 java concurrency 上就有。
esolve
2017-03-02 00:02:26 +08:00
@rogerchen

这里有一个例子( http://blog.csdn.net/zhangheliang2010/article/details/44890103)

public class MyStack {
private List<String> list = new ArrayList<String>();

public synchronized void push(String value) {
synchronized (this) {
list.add(value);
notify();
}
}

public synchronized String pop() throws InterruptedException {
synchronized (this) {
if (list.size() <= 0) {
wait();
}
return list.remove(list.size() - 1);
}
}
}

问题: 这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?

代码分析:

从整体上,在并发状态下, push 和 pop 都使用了 synchronized 的锁,来实现同步,同步的数据对象是基于 List 的数据;大部分情况下是可以正常工作的。

问题描述:

状况 1 :

1. 假设有三个线程: A,B,C. A 负责放入数据到 list,就是调用 push 操作, B,C 分别执行 Pop 操作,移除数据。

2. 首先 B 先执行,于 pop 中的 wait()方法处,进入 waiting 状态,进入等待队列,释放锁。

3. A 首先执行放入数据 push 操作到 List ,在调用 notify()之前; 同时 C 执行 pop(),由于 synchronized ,被阻塞,进入 Blocked 状态,放入基于锁的等待队列。注意,这里的队列和 2 中的 waiting 等待队列是两个不同的队列。

4. A 线程调用 notify(),唤醒等待中的线程 A 。

5. 如果此时, C 获取到基于对象的锁,则优先执行,执行 pop 方法,获取数据,从 list 移除一个元素。

6. 然后, A 获取到竞争锁, A 中调用 list.remove(list.size() - 1),则会报数据越界 exception 。

状况 2 :

1. 相同于状况 1

2. B 、 C 都处于等待 waiting 状态,释放锁。等待 notify()、 notifyAll()操作的唤醒。

3. 存在被虚假唤醒的可能。

何为虚假唤醒?

虚假唤醒就是一些 obj.wait()会在除了 obj.notify()和 obj.notifyAll()的其他情况被唤醒,而此时是不应该唤醒的。

------------------------------------------------------------

我的理解是, A 被 B 的 notify()唤醒了,但是条件变量不满足了,所以这个虚假指的是条件变量不满足所以虚假

但是原文中却说:“一些 obj.wait()会在除了 obj.notify()和 obj.notifyAll()的其他情况被唤醒”

着我就不懂了,明明 A 是被 B 的 notify()唤醒了啊,什么叫“除了 obj.notify()和 obj.notifyAll()的其他情况被唤醒”?
choury
2017-03-02 00:33:36 +08:00
我一般会在 wait 外面套 while ,而不是 if
linbiaye
2017-03-02 07:32:20 +08:00
Linux man 手册
"Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return."

Oracle 文档:
http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28%29

"As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
"


poxis 的 pthread_cond_wait 就是有 suspicious wakeup ,你要问为什么会有,我只能说无可奉告。你唯一要做的就是在循环里面检测 condition 。
domty
2017-03-02 15:05:41 +08:00
@esolve #4
只有我认为他在状况里的内容写错了吗?

"4. A 线程调用 notify(),唤醒等待中的线程 A 。"
应该是 "A 线程调用 notify(),唤醒等待中的线程 B "

"6. 然后, A 获取到竞争锁, A 中调用 list.remove(list.size() - 1),则会报数据越界 exception 。"
应该是"6. 然后, B 获取到竞争锁, B 中调用 list.remove(list.size() - 1),则会报数据越界 exception 。"

才对吧?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/344154

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX