V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
esolve
V2EX  ›  Java

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

  •  
  •   esolve · 2017-03-01 19:45:53 +08:00 · 3413 次点击
    这是一个创建于 2853 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    7 条回复    2017-03-02 15:05:41 +08:00
    kaneg
        1
    kaneg  
       2017-03-01 19:47:23 +08:00 via iPhone
    你到底在说啥?请用专业术语
    snnn
        2
    snnn  
       2017-03-01 20:05:23 +08:00 via Android
    听不懂
    rogerchen
        3
    rogerchen  
       2017-03-01 21:33:49 +08:00
    @kaneg
    @snnn
    说的是 Spurious wakeup

    @esolve
    就是 cv 返回了也不一定可以拿 mutex ,还是要检测。这是多线程编程基础,跟语言关系不大, java 例子那本 java concurrency 上就有。
    esolve
        4
    esolve  
    OP
       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
        5
    choury  
       2017-03-02 00:33:36 +08:00
    我一般会在 wait 外面套 while ,而不是 if
    linbiaye
        6
    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
        7
    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 。"

    才对吧?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3403 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 278ms · UTC 11:21 · PVG 19:21 · LAX 03:21 · JFK 06:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.