资讯专栏INFORMATION COLUMN

Java中的wait/notify/notifyAll

terasum / 3246人阅读

摘要:等待一段时间是否有线程唤醒锁,如果没有,超时自动唤醒。随机唤醒等待队列中的等待同一个锁的一个线程,使这个线程退出等待队列,进入可运行状态。条件队列中是处于等待状态的线程,等待特定条件为真。在一般情况下,总应该调用唤醒所有需要被唤醒的线程。

方法

java.lang.Object

public final native void wait() throws InterruptedException;
public final native void wait(long millis, int nanos) throws InterruptedException;
public final void wait(long millis) throws InterruptedException {
    wait(millis, 0);
}

public final native void notify();
public final native void notifyAll();

wait():使调用该方法的线程释放锁,从运行状态退出,进入等待队列,直到被唤醒。

wait(long timeout):等待一段时间是否有线程唤醒锁,如果没有,超时自动唤醒。

wait(long timeout, int nanos):等待唤醒时间纳秒级别。

notify():随机唤醒等待队列中的等待同一个锁的一个线程,使这个线程退出等待队列,进入可运行状态。

notifyAll():唤醒所有等待同样锁的所有线程,从等待队列中退出,进入可运行状态。

注意点

在调用wait或者notify之前,必须获得该对象的对象锁,即,只能在同步方法中调用;

执行完wait之后释放对象锁,所以其他线程可以获得执行机会,才能唤醒;

执行notify之后,不会立即退出让wait的线程执行,必须要先把同步块中的程序执行完,退出同步块,才会释放锁,让等待线程执行;

notify每次通知一个线程,多次调用通知线程数增加,可将wait线程全部唤醒。

原理

每个对象都有个monitor,初始是0,执行完synchronized值就是1。

wait/notify需要在获得monitor的线程中才可以执行。

所以,wait/notify需要在synchronized中执行

其中,wait又会释放掉锁,破坏掉同步。

跟synchronized关系

synchronized代码块生成的字节码,被monitorentermonitorexit包围,持有对象的monitor;

线程执行wait/notify方法时,必须持有对象的monitor;

所以,wait/notify方法在synchronized同步块中执行,就持有了对象的锁。

互斥和协同

Java语言的同步机制在底层实现上只有两种方式:互斥和协同。

互斥:即synchronized内置锁。

协同:即内置条件队列,wait/notify/notifyAll。

条件队列中是处于等待状态的线程,等待特定条件为真。每个Java对象都可以作为一个锁,同样每个Java对象都可以作为一个条件队列。通过wait/notify/notifyAll来操作条件队列。

可以理解为:有一个队列,o.wait()就push进去,o.notify()就pull出来。

要调用条件队列的任何一个方法,都必须要获得对象上的锁。

线程是用来工作的,不应该处于等待状态,处于等待状态的条件队列中的线程,一定是执行不下去的。

在while中等待
while(condition is not true) {
  lock.wait()
}

解释:两个消费者线程c1和c2,逻辑都是,判断资源是否为空,是就wait,否就消费一个;某个时刻,两个线程都进入等待队列,然后生产者生产了一个资源,并执行notifyAll,唤醒c1和c2都进入锁池,c1先获取锁,执行完消费掉资源,然后释放锁,此时,如果c2获得锁,如果是if逻辑,那么就会进入消费代码,但是资源已经被c1消费掉了,可能抛出异常。如果是while逻辑,则不会进入消费代码,而是继续等待。

在一般情况下,总应该调用notifyAll唤醒所有需要被唤醒的线程。可能会唤醒其他一些线程,但这不影响程序的正确性,这些线程醒来之后,会检查他们正在等待的条件(循环检测),如果发现条件不满足,就会继续等待

显示锁和显示条件队列

显示锁:Lock,对应内置锁synchronized

显示条件队列:Condition,对应内置条件队列,对应方法是await, signal, signalAll

问题

notifyAll唤醒所有线程,但不是所有线程都能执行,必须要等待对象锁被释放,获取锁之后才能执行。可以说,notifyAll让线程进入锁池。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70909.html

相关文章

  • [Java并发-5]用“等待-通知”机制优化循环等待

    摘要:在这个等待通知机制中,我们需要考虑以下四个要素。何时等待线程要求的条件不满足就等待。是会随机地通知等待队列中的一个线程,而会通知等待队列中的所有线程。 由上一篇文章你应该已经知道,在 破坏占用且等待条件 的时候,如果转出账本和转入账本不满足同时在文件架上这个条件,就用死循环的方式来循环等待,核心代码如下: // 一次性申请转出账户和转入账户,直到成功 while(!actr.apply...

    fxp 评论0 收藏0
  • 线程间的同步与通信(2)——wait, notify, notifyAll

    摘要:前言上一篇文章我们讲了的同步代码块这一篇我们来看看同步代码块之间的协作与通信阅读本篇前你需要知道什么是同步代码块什么是监视器锁还不是很了解的同学建议先去看一看上一篇文章本文的源码基于系列文章目录概述在中我们可以使用这个方法来实现同步代码块之 前言 上一篇文章我们讲了java的同步代码块, 这一篇我们来看看同步代码块之间的协作与通信. 阅读本篇前你需要知道什么是同步代码块, 什么是监视器...

    djfml 评论0 收藏0
  • java并发编程学习之线程的生命周期-wait,notify,notifyall(六)

    摘要:不释放持有的锁,释放锁。在调用方法前,必须持有锁,调用唤醒,也要持有锁。休眠一定时间后,进入就绪状态。这两个都能被方法中断当前状态。用法方获取锁判断条件,不满足继续满足执行其他业务方获取锁改变条件通知为什么是而不是会一直循环,直到条件满足。 sleep和wait sleep是Thread类的方法,wait是Object的方法。 sleep可以到处使用,wait必须是在同步方法或者代码...

    Terry_Tai 评论0 收藏0
  • 一起学并发编程 - 等待与通知

    摘要:如果有其它线程调用了相同对象的方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,从新争夺锁的拥有权。 wait,notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视,而本文则是对这些关键字的使用进行描述。 存在即合理 在java中,每个对象都有两个池,锁池(monitor)和等待池(waitset),每个...

    Meathill 评论0 收藏0
  • JavawaitnotifynotifyAll使用详解

    摘要:用法中规定,在调用者三个方法时,当前线程必须获得对象锁。作用方法作用线程自动释放占有的对象锁,并等待。当生产者生产了一个数据或者消费者消费了一个数据之后,使用方法来通知所有等待当前对象锁的线程,但是一次只会有一个等待的线程能拿到锁。 基础知识 首先我们需要知道,这几个都是Object对象的方法。换言之,Java中所有的对象都有这些方法。 public final native void...

    rozbo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<