`
jackyhongvip
  • 浏览: 159696 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

wait和notify机制

    博客分类:
  • j2se
 
阅读更多

Wait-Notify机制可以说是实现阻塞操作较为高效的一种方式。

典型的Wait-Notify场景一般与以下内容相关

1.状态变量(State Variable)

当线程需要wait的时候,总是因为一些状态不满足导致的。如往BlockQueue里面加入元素已满的时候。

2.条件断言(Condition Predicate)

当线程确定是否进入wait或者从notify中醒来的时候是否往下执行,大都要测试状态是否满足,如往BlockQueue里面加入元素已满,于是阻塞,后续其他线程从队列里取走了元素,于是在等待的线程“队列不是满的了,可以往里面加东西了”,这时候在等待的线程会醒来,然后看看是不是队列不为满的状态,如果是则将元素家进入,如果不是,就继续等待。

3.条件队列(Condition Queue)

每个对象都有一个内置的条件队列,当一个线程调用该对象的wait方法的时候,就将该线程加入到了该对象的条件队列了。

 

 

基于以上来谈wait和notify操作。在调用wait、notify的时候,必须先持有锁,且状态变量必须由该锁保护,而内置锁对象和内置条件队列对象又是同一个对象。也就是说,要在某个对象上执行wait、notify先必须锁定该对象,而对应的状态变量也是由该对象锁保护的,如果在调用wait、notify的时候没有持有锁,将会抛出一下错误:

Exception....java.lang.IllegalMonitorStateException

 

 

线程的wait操作的典型代码结构如下:

void op() throws InterruptedException {
    synchronized(obj) {
        while(条件不满足) {
            obj.wait();
        }
    }
}

为什么要在循环中wait?有以下几个原因。
1、一个对象锁可能用于保护多个状态变量,当它们都需要wait-notify操作时,如果不将wait放到while中就有问题。例如,某对象锁obj保护两种状态变量a和b,当a的条件断言不成立时发生了wait操作,当b的条件断言不成立时也发生了wait操作,两个线程被加入到obj对应的条件队列中,现在若改变状态变量a的某操作发生,在obj上调用了notifyAll操作,obj对应的条件队列里的所有线程均被唤醒,之前等待a的某个或几个线程去判断a的条件断言可能成立了,但b对应的条件断言肯定仍不成立,而此时等待b的线程也被唤醒了,所以需要循环判断b的条件断言是否满足,如果不满足,继续wait。
2、多个线程wait的同一个状态的条件断言。如BlockingQueue场景下,当前队列是空的,多个线程要从里面取元素,于是都wait了。此时另一个线程往里面添加了一个元素,调用了notifyAll操作,唤醒了所有线程,但只有一个线程能拿到那个新加进来的元素,继续走下去,其它的仍需等待。
3、虚假唤醒。在没有被通知、中断或超时的情况下,线程自动苏醒了。虽然这种情况在实践中很少发生,但是必须通过循环检测条件是否满足的方式来防止其发生,如果不满足该条件,则继续等待。

notify操作有两个方法可用,nofity和notifyAll,顾名思义,前者每次唤醒一个线程,后者唤醒所有线程。当唤醒所有线程的时候,会增加上下文切换、锁竞争。但很多时候,使用notify是有风险的,多个线程在同一个条件队列里等待不同的条件断言成立,极可能本该唤醒的线程没唤醒。那么什么时候才能用notify呢?牛人们已经总结好了,需要满足以下两个条件:
1、该对象的条件队列只关联了一个条件断言,且线程被唤醒后执行的代码逻辑是相同的;
2、单进单出。一次notify(这里不是指notify方法)能唤醒的线程至多一个。

对于第一点,在为什么要循环中wait以及为什么notify方法有风险时已经说过了。对于第二点,比如要实现一个类似开/关锁存器(在构造CountDownLatch的时候传入1)的功能,所有线程调用await()操作,最终某一线程调用countDown()操作,该countDown()操作就需要唤醒所有wait的线程。在这种场景下,第二条是不满足的——使用notify方法,其它线程将无法唤醒。

上面说到的都是内置锁,内置条件队列,与之对应的,有显式锁(Lock),显式条件队列(Lock#newCondition())
Lock#newCondition()返回一个Condition对象,该对象上对应于操作条件队列的wait和notify方法为:await、signal。与内置锁一样,要调用Condition的await、signal,需要锁定创建该Condition的Lock。如下代码形式:

package com.ticmy.concurrency;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestWatiNotifyMechanism {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    public static void main(String[] args) throws Exception {
        //无意义,仅测试用,勿模仿
        lock.lock();
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
    }
}

除了这个对应关系不一样,其他诸如需要在循环中await,以及使用signal()还是signalAll()等原理与前面一致,不再赘述。需要说到的是,它与内置锁、内置条件队列的区别。
1、内置锁对象只有一个条件队列,而显式锁可以通过newCondition方法创建多个条件队列,这样就可以避免不同的条件断言关联同一个条件队列造成的问题。
2、如同Lock比内置锁更灵活一样,显式的条件队列也提供了更多的方法供调用(如等待的时候不可被中断的awaitUninterruptibly方法),更多方法参见java.util.concurrent.locks.Condition的JAVA API。
3、Condition也有wait、notify方法,它们从Object类继承而来,一般实际中不会调用这些方法(要调用这些方法必须持有Condition对象的锁,而不是Lock的锁定)以避免混淆。
4、Condition可以继承Lock的公平策略。如new ReentrantLock的时候传入的公平策略参数。当公平策略为true的时候,signal的时候,Condition中的线程唤醒顺序是FIFO的。

 

 

 

分享到:
评论

相关推荐

    Java 同步方式 wait和notify/notifyall

    在Java中,`wait()`, `notify()`, 和 `notifyAll()` 是Java Object类的三个方法,它们在实现线程间通信和协作时扮演着关键角色。这些方法主要用于解决线程等待和唤醒的问题,是基于Java Monitor(监视器)模型的。 ...

    浅谈java多线程wait,notify

    _java多线程wait、notify机制详解_ 在Java多线程编程中,wait和notify是两个非常重要的机制,用于实现线程之间的...通过这篇文章,读者可以更好地理解和掌握wait和notify机制,从而更好地编写高效、可靠的多线程程序。

    Java多线程wait和notify

    总结来说,Java的 `wait()` 和 `notify()` 提供了一种在多线程环境中控制线程执行的机制。通过合理使用这些方法,我们可以实现线程间的协作,精确控制子线程的运行状态。然而,这种方式虽然灵活,但管理起来相对复杂...

    wait_notify_demo

    `wait()`、`notify()`和`notifyAll()`是Java中的三个关键字,它们属于Object类的方法,主要用于线程间的通信,尤其在实现生产者消费者模式时发挥着重要作用。本文将深入探讨这些方法以及如何在实际场景中应用它们。 ...

    深入理解Wait、Notify和Wait与sleep区别

    - **唤醒机制**:`wait()`需要其他线程调用`notify()`或`notifyAll()`来唤醒,而`sleep()`结束后会自动恢复执行。 - **唤醒策略**:`wait()`可能唤醒单个或所有等待线程,而`sleep()`结束后自动唤醒自身。 在实际...

    详解Java程序并发的Wait-Notify机制

    Java程序并发的Wait-Notify机制是Java多线程编程中的一种重要同步工具,它允许线程之间通过共享对象进行通信和协作。这个机制基于Java的内置锁(也称为监视器锁),通常与`synchronized`关键字一起使用。在Java中,`...

    wait,notify等线程知识.pdf

    使用wait/notify机制时,需要注意死锁和活锁问题。死锁发生在两个或更多线程互相等待对方释放资源的情况下。活锁则是线程不断尝试获取资源但一直失败,导致无限期阻塞。合理设计同步策略和避免这些情况是多线程编程...

    等待机制与锁机制wait notify

    `wait()`、`notify()`和`notifyAll()`是Java `Object`类的三个方法,它们用于线程间的通信。这些方法必须在同步环境中(即synchronized方法或synchronized代码块)使用,否则会抛出`IllegalMonitorStateException`...

    一个理解wait()与notify()的例子

    本文旨在解析一个具体的Java多线程示例代码,以帮助读者更好地理解`wait()`与`notify()`方法的作用及其实现机制。这两个方法是Java中实现线程间通信的重要手段之一,尤其在解决生产者消费者模型、读者写者问题等经典...

    38.线程间的通信-wait与notify-wait与notifty机制的实现.mp4

    在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。

    Java 同步锁 wait notify 学习心得

    标题和描述概述的知识点主要集中在Java的多线程机制中,特别是`wait`和`notify`方法在同步锁中的应用。这些方法对于控制线程之间的交互至关重要,尤其是在资源有限或需要确保数据一致性的情况下。 ### Java同步锁...

    java线程-保护性暂停(wait,notify实现).docx

    本文将详细讲解 Java 线程中的保护性暂停机制,即 wait 和 notify 方法的实现。在 Java 中,线程之间的通信是一个非常重要的课题,而保护性暂停机制正是解决线程之间通信的关键所在。 首先,让我们来看一下上述场景...

    基于令牌桶算法的Java限流实现

    同时,我们还可以使用wait和notify机制来实现线程之间的通信,例如,在令牌桶中没有资源时,线程可以等待一段时间,直到有新的资源被添加到令牌桶中。 在Java代码中,我们可以使用以下方式来实现限流器: ```java ...

    Object类wait及notify方法原理实例解析

    Object类中的wait和notify方法是Java多线程编程中最重要的同步机制之一,它们是Java.lang.Object类中的两个方法,用于在多线程之间进行通信和同步。wait方法将当前线程置于等待状态,而notify方法则用于唤醒等待的...

    浅谈Java线程间通信之wait/notify

    在实际开发中,我们可以使用wait/notify机制来实现各种类型的线程间通信,例如生产者/消费者模式、读者/写者模式等等。 wait/notify机制是Java多线程编程中非常重要的一部分,掌握它可以帮助我们更好地编写高效、...

    多线程面试专题-答案.pdf

    实现阻塞队列通常需要使用wait和notify机制,或者Java 5以后提供的并发包中的AbstractQueueSynchronizer。在面试中,面试官可能会要求候选人用不同方式实现阻塞队列,并进行讨论。 5. 生产者-消费者问题 生产者-...

    一线互联网大厂完整Java面试题.pdf

    因为在Java中,任何对象都可以作为一个监视器,wait和notify机制需要依托于对象锁,所以定义在Object类中可以实现该机制。 2. wait方法需要在synchronized方法或代码块中调用的原因是wait方法会导致当前线程释放对象...

    一家三口共用同一账户银行卡,wait();notify();

    5. **线程安全**:通过使用synchronized关键字和wait/notify机制,保证了在多线程环境下,存款和取款操作的正确性,避免了数据竞争和死锁等问题。 6. **线程启动**:通过调用`Thread.start()`启动线程,使得新线程...

    详细分析java线程wait和notify

    Java中的`wait()`和`notify()`方法是多线程编程中的关键工具,用于线程间的同步和通信。这两个方法都是Object类的成员,这意味着所有Java对象都可以使用它们。`wait()`方法使当前线程暂停执行并释放它持有的锁,直到...

Global site tag (gtag.js) - Google Analytics