Java 多线程问题

2020-05-30 15:46:29 +08:00
 linxiaoziruo

为什么使用 notify 和 notifyAll 的时候要先获得锁?

2600 次点击
所在节点    Java
13 条回复
cs419
2020-05-30 17:19:43 +08:00
没这个说法 不搭嘎

你想想
1. 为啥要用锁,不用锁会出啥问题 (原子性)
2. 面对这个问题 怎么用 notify 去解决 (线程等待)

如果确实要线程等待
一般不用 notify 用 LockSupport
linxiaoziruo
2020-05-30 19:35:11 +08:00
@cs419 怎么会不搭嘎呢,使用 notify 和 notifyAll 的时候必须要在 syncnorize 的临界区内,即必须先获得这个对象锁才能执行 notify,否则会抛出 IllegalMonitorStateException
linxiaoziruo
2020-05-30 19:38:58 +08:00
@cs419 锁是解决共享资源竞争引起的线程安全问题,和原子性有什么关联呢,原子性指的是不能中断的操作,和锁是两个没有关联的概念。
23571113
2020-05-30 20:25:49 +08:00
notify 就是通知释放锁啊, 你没锁怎么释放.
pursuer
2020-05-30 20:27:30 +08:00
notify,wait,和条件判断通常是一起用的,条件判断后选择进入等待或继续运行,这个时候要保证条件判断后,条件不会被其他线程改变,所以通常都会有个锁保障类似的场景。
sioncheng
2020-05-30 21:50:39 +08:00
notify \ wait 是一种协作吧,双方需要能获得相同的锁才能说明是是在协作干一个事情,要不然,不相干的参与者瞎 notify 能行吗
lux182
2020-05-30 23:57:01 +08:00
释放 monitor
唤醒等待队列的线程
seki4713
2020-05-31 14:23:47 +08:00
防止你条件判断成功后突然被抢占 然后条件被修改 保证判断条件到进入临界区的整个操作是原子的
bigbyto
2020-05-31 20:48:34 +08:00
这个问题其实真不好回答,就像大家习以为常的问题,突然被问为什么了。其实上面的老哥都没回答到点上,我觉得为什么 notify 必须要 synchorized,根本原因在于 wait set 的操作是原子的。先看看 JLS 对 wait set 的描述。

> Every object, in addition to having an associated monitor, has an associated *wait set*. A wait set is a set of threads.
>
> When an object is first created, its wait set is empty. **Elementary actions that add threads to and remove threads from wait sets are atomic**. Wait sets are manipulated solely through the methods `Object.wait`, `Object.notify`, and `Object.notifyAll`.
>
> Ref: https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html

简单来说,当对象创建时,会顺带创建 Monitor 和 Wait Set,这些应该是在 C 语言层面去创建的。然后它告诉我们对 Wait Set 的操作都是 Atomic 的,这就能解释,为什么 wait 和 notify 必须获得锁,因为没有锁,就没办法保证对 wait set 的操作是原子的。
JasonLaw
2020-06-01 08:19:03 +08:00
来自 Java Concurrency in Practice 。

Just as each Java object can act as a lock, each object can also act as a condition queue, and the wait, notify, and notifyAll methods in Object constitute the API for intrinsic condition queues. An object’s intrinsic lock and its intrinsic condition queue are related: in order to call any of the condition queue methods on object X, you must hold the lock on X. This is because the mechanism for waiting for state-based conditions is necessarily tightly bound to the mechanism for preserving state consistency: you cannot wait for a condition unless you can examine the state, and you cannot release another thread from a condition wait unless you can modify the state.

Object.wait atomically releases the lock and asks the OS to suspend the current thread, allowing other threads to acquire the lock and therefore modify the object state. Upon waking, it reacquires the lock before returning. Intuitively, calling wait means “I want to go to sleep, but wake me when something interesting happens”, and calling the notification methods means “something interesting happened”.
willxiang
2020-06-01 09:14:46 +08:00
"必须在 synchronized 块中才能调用 wait()方法,因为 wait()方法调用时,会释放线程获得的锁,wait()方法返回后,线程又会重新试图获得锁。"

https://www.liaoxuefeng.com/wiki/1252599548343744/1306580911915042
cs419
2020-06-01 10:28:56 +08:00
忽然发现被降权了 没收到消息通知
奇怪的知识增加了
没觉着 wait 好用
平时要么普通的多线程 要么 LockSupport 要么 JUC
Kamiyu0087
2020-06-01 11:33:58 +08:00
直接从实际使用的情况来看的话,你如果 notify 的时候不去获得锁的话,没法保证 notify 在 wait 之后再执行啊
```java
final Object lock = new Object();
synchronized (lock) {
new Thread(() -> lock.notify()).start();

try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
```

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

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

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

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

© 2021 V2EX