线程通信——等待通知机制

  |   0 评论   |   0 浏览

一个线程修改了一个值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程。

前者是生产者,后者是消费者,这种模式在功能上实现了解耦Java 语言中如何实现类似的功能呢

简单的办法是让消费者线程不断地循环检查变量是否符合预期。

while(value != desire){
  Thread.sleep(10000);
}
doSomething();

上述代码存在以下问题:

    1. 难以确保及时性。在睡眠时,基本不消耗处理器资源,但是如果睡得太久,就不能及时发现条件已经变化,也就是及时性难以保证。
  • 2)难以降低开销。如果降低睡眠的时间,比如休眠 1 毫秒,这样消费者能更加迅速地发现条件变化,但是却可能消耗更多地处理器资源,造成了无端的浪费。

Java 通过内置的等待/通知机制能够很好的解决这个矛盾并实现所需的功能。

等待/通知的相关方法是任意 Java 对象都具备的。这些方法定义在 Object 类中

等待通知机制的相关方法

方法名描述
notify()通知一个对象上等待的线程,使其从 wait() 方法返回,而返回的前提是该线程获取到了对象的锁
notifyAll()通知所有在该对象上等待的线程
wait()调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用 wait() 方法后,会释放对象的锁
wait(long)超时等待一段时间,这里的参数时间是毫秒,也就是等待长 n 毫秒,如果没有通知就超时返回。
wait(long,int)对于超时时间更细粒度的控制,可以达到纳秒

等待/通知机制,是指一个线程 A 调用了对象 O 的 wait() 方法进入等待状态,而另一个线程 B 调用了对象 O 的 notify() 或者 notifyAll() 方法,线程 A 收到通知后从对象 O 的 wait() 方法返回,进而执行后续的操作。

使用 wait()、notify() 以及 notifyAll() 需要注意的细节

  • 1)使用 wait()、notify() 和 notifyAll() 是需要先对调用对象加锁
  • 2)调用 wait() 方法后,线程状态由 RUNNING 变为 WAITING, 并将当前线程放置到对象的等待队列
  • 3)notify() 和 notifyAll() 方法被调用后,等待线程依旧不会从 wait() 返回,需要调用 notify() 或 notifyAll() 的线程释放锁之后,等待线程才有机会从 wait() 返回
  • 4)notify() 方法将等待队列中的一个等待线程从等待队列中移动到同步队列中, 而 notifyAll() 方法则是将等待队列中所有的线程全部移动到 同步队列,被移动的线程状态由 WAITING 状态 变未 BLOCKED
  • 5)从 wait() 方法返回的前提是获得了调用对象的锁

等待通知的经典范式

范式分为两部分,分别是等待方(消费者) 和 通知方(生产者)

等待方遵循如下原则。

  • 1)获取对象的锁

  • 2)如果条件不满足,那么调用对象的 wait() 方法,被通知后仍要检查条件

  • 3)条件满足则执行对应的逻辑

对应的伪代码如下:

synchronized(对象){
    while(条件不满足){
         对象.wait();
    }
    对应的处理逻辑
}

通知方遵循如下原则:

    1. 获取对象的锁
    1. 改变条件
    1. 通知所有等待在对象上的线程
synchronized(对象){
    改变条件
    对象.notifyAll();
}

等待超时模式

开发人员经常会遇到这样的方法调用场景:调用一个方法时,等待一段时间(一般来说是给定一个时间段),如果该方法能够在给定的时间段之内得到结果,那么将结果立刻返回,反之,超时返回默认结果

超时等待的加入,只需要对经典范式做出非常小的改动。

假设超时时间段是 T,那么可以推断出在当前时间now + T 之后会超时

定义如下变量。

  • 等待持续时间:REMAINING = T

  • 超时时间: FUTURE = now + T

这是仅需要 wait(REMAINING)即可,在 wait(REMAINING) 返回之后会执行:
REMAINING = FUTURE - now。 如果 REMAINING 小于等于 0,表示已经超时,直接退出,否则将继续执行 wait(REMAINING)。

上述等待超时模式的伪代码如下.

public synchronzied Object get(long mills) throws InterruptedException{
     long  future = System.currentTimeMillis() + mills;
     long  remaining = mills;
     // 当超时时间大于 0,并且 result 返回值不满足要求
     while((result == null) && remaining > 0){
          wait(remaining);
          remaining  = future - System.currentTimeMillis();
     }
     return result;
}

标题:线程通信——等待通知机制
作者:zh847707713
地址:http://lovehao.cn/articles/2020/03/09/1583758158024.html