为什么waitnotify必须要强制要求放在synchronized中

45次阅读

共计 1448 个字符,预计需要花费 4 分钟才能阅读完成。

1 为什么 wait/notify 必须要强制要求放在 synchronized 中

在日常开发,咱们都晓得 wait/notify 有着十分固定的一套模板,就是上面的样子,synchronized 同步块包裹着 Object.wait() 办法,如果不通过同步块包住的话 JVM 会抛出 IllegalMonitorStateException 异样。

synchronized(lock) {while(!condition){lock.wait();
    }
}

那么为什么要限度这么写呢?

2 如果 Object.wait()/notify 不须要同步

假如咱们本人实现了一个 BlockingQueue 的代码。
如果 Object.wait()/notify 不须要同步,那么咱们的代码会形如上面这样。

class BlockingQueue {Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {buffer.add(data);
        notify();                   // 往队列里增加的时候 notify,因为可能有人在等着 take}

    public String take() throws InterruptedException {while (buffer.isEmpty())    // 用 while,避免 spurious wakeups(虚伪唤醒)wait(); // 当 buffer 是空的时候就等着他人 give
        return buffer.remove();}
}

如果下面的代码能够执行的话,多线程状况下 会呈现一种状况:

  1. 以后 buffer 是空的,这时来了一个 take 的申请,尚未执行到 wait 语句
  2. 同时又来了一个 give 申请,残缺 执行完了整个 give 办法并且发送了 notify
  3. 此时 take 办法才走到 wait,因为它 错过了上一个 notify,所以会在明明 buffer 不空的状况下挂起线程,take 办法挂起。如果再没有人调用过 give 办法了,在业务上的体现就会是这个 take 线程永远也取不到 buffer 中的内容

3 为什么要在 JVM 层面抛异样

因为 你只有用 notify,那就是为了在多线程环境下同步,notify/wait 机制自身就是为了多线程的同步而存在的,那就只能配套 synchronized,所以为了避免下面状况的产生,就间接强制抛异样来限度开发的代码模式了。

4 如果没有 wait/notify

试想一种场景,如果没有 wait/notify 的挂起唤醒机制,该如何实现 BlockingQueue

class BlockingQueue {Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {buffer.add(data);
    }

    public String take() throws InterruptedException {while(buffer.isEmpty) {sleep(10);
        }
        return buffer.remove();}
}

如果没有 wait/notify,那么在 take 的时候只能通过 while 循环不停轮询判断 buffer 是否为空来实时获取 buffer 的最新状态,那么势必会造成两种状况:

  1. sleep 工夫过短,那么线程将始终不行循环抢占 CPU,造成 CPU 打满
  2. sleep 工夫过长,那么将会影响 take 的时效性

综上,针对于 BlockingQueue 这样的场景,同步块 + wait/notify 或者 lock + signal/await 就是标配

正文完
 0