关于wait,notify,notifyall,sleep方法的思考
Posted 写Bug的渣渣高
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于wait,notify,notifyall,sleep方法的思考相关的知识,希望对你有一定的参考价值。
本文概述:
你可能对这几个方法, 已经特别熟悉了. 但是你知道吗
- 为什么 wait 方法和 notify, notifyAll 定义在 Object 中,sleep 定义在 Thread 中呢
- 为什么 wait 方法和 notify 方法需要在同步代码块中使用 (如果不在同步代码块使用会怎么样)
- wait, notify 和 sleep 方法的异同
- 为什么 wait/notify/notifyAll 被定义在 Object 类中, 而 Sleep 定义在 Thread 中呢?
- ==如果 wait 方法和 notify 方法不是必须在同步代码块中呢=
wait/notify 和 sleep 方法的异同?
相同点:
- 它们都可以让线程阻塞。
- 它们都可以响应 interrupt 中断:在等待的过程中如果收到中断信号,都可以进行响应,并抛出InterruptedException 异常。
不同点: - wait 方法必须在 synchronized 保护的代码中使用,而 sleep 方法并没有这个要求。
- 在同步代码中执行 sleep 方法时,并不会释放 monitor 锁,但执行 wait 方法时会主动释放
monitor 锁。 - sleep 方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的 wait 方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复。
- wait/notify 是 Object 类的方法,而 sleep 是 Thread 类的方法。
为什么 wait/notify/notifyAll 被定义在 Object 类中, 而 Sleep 定义在 Thread 中呢?
这里有些前缀知识, 我会简要说明.
如果你了解过 syncrhonized 关键字, 那么就了解, 他可以修饰普通方法, 静态方法, 代码块.
synchronized 底层实现是由 Monitor 来实现的, 如果你不懂, 也只需要看我这里说的即可.
![[Pasted image 20230221094004.png]]
当一个线程进入 synchronized 之后, 底层的 Mointor 对象的 _owner 会记录拥有锁的线程, 其他线程查看的时候都不会进入同步代码块, 这就是锁的原来. 而其可重入性, 也是基于这个原因
持有锁的线程结束之后, 才会释放锁, 其他线程去抢锁.
synchronized 的锁有三种:
- synchronized 修饰普通方法, 锁为当前的实例对象本身
- synchronized: 修饰静态方法, 锁位当前类的 Class 对象
- synchronized 修饰代码块, 可以为自定义的 Object 或者是 this 关键字.
Ok, 前缀知识介绍完毕, 如果你希望了解关于这部分的 synchronized 的轻量锁, 偏向锁, 重量锁, 自适应自旋等, 可以去搜索相关了解, 博主后续也会写该部分的博客.
然后了解一下这些方法吧
- wait: 释放锁, 当前线程进入等待状态
- notify: 唤醒一个等待线程
- notifyAll: 唤醒所有等待的线程
- sleep: 当前线程进入等待
为什么 wait/notify/notifyAll 要设计在 Object 中呢?
- 为什么不在 Thread 类中呢? 因为一个线程可能持有多个锁, 比如说一个线程连续执行了两个加了锁的方法, 此时他都获取到了不同的锁对吧, 如果此时调用 wait 或者是调用 notify/notifyAll 方法, 如何确定是对哪个锁进行操作. 并且 wait 之后, 是不是要进入等待状态啊, 进入等待状态, 你得释放全部锁, 不然就死锁了. 如果每次都要释放全部锁, 那么多线程的设计会变得特别粗糙, 比如说一个线程获取了三个锁, 调用 wait 之后, 再次唤醒, 却需要重新和所有的线程竞争锁, 执行时间会变得很慢
wait 和 notify 必须写在同步代码块中
这部分来想象一下, 如果 wait 以及 notify 方法不写在同步代码块中, 会发生什么现象
如果,wait 和 notify 不是在同步代码块中, 就会出现很严重的现象, 在多线程环境中, 我们无法确定 wait 和 notify 哪个先执行, 假如 wait 先执行,notify 后执行, 如果每次都是这样, 线程是能够成功休眠, 也能成功的唤醒的.
但是假如 notify 先执行了,wait 后执行, 这时候就会进入永久等待
来看一个例子:
class BlockingQueue
Queue<String> buffer = new LinkedList<String>();
public void give(String data)
buffer.add(data);
notify();
public String take() throws InterruptedException
while (buffer.isEmpty())
wait();
return buffer.remove();
上面是在非同步代码块中调用 wait 和 notify 的场景, 事实上上面的代码就是错误的, 此时假如能执行, 想象一下会发生什么?
上面是一个生产者/消费者模型, 调用 give 就往队列中加入元素, 调用 take 就从队列中取出来一个元素.
如果 buffer. isEmpty () 为 true, 那么就阻塞, 如果在多线程中, notify 先执行, 然后 wait 后执行, 这个时候, 如果后续没有 notify 执行, take ()方法就会永久阻塞住.
Sleep 为什么不定义在 Object 类中
嗯… 是个好问题, 没有必要, 线程的 Sleep 休眠状态和 Object 没太大的关系.
以上是关于关于wait,notify,notifyall,sleep方法的思考的主要内容,如果未能解决你的问题,请参考以下文章
多线程(四)wait()notify()以及notifyAll()
java condition await signal signalall对比wait notify notifyall