wait(),notify() 与 await(), signal(), signalAll() 的区别

  • Post author:
  • Post category:其他


参考文章


Oracle 官方文档 Condition Interface



How is await() different from wait() ?

wait() 和 notify() 的使用方式


  • wait()



    notify()

    需要搭配

    synchronized

    关键字使用, 示例如下
// 线程 A 的代码
synchronized(obj_A)
{
    while(!condition){ 
        obj_A.wait();
    }
    // do something 
}
// 线程 B 的代码
synchronized(obj_A)
{
    if(!condition){ 
        // do something ...
        condition = true;
        obj_A.notify();
    }
}
  • 为什么

    wait()

    ,

    notify()

    需要搭配

    synchronized

    关键字使用

    • wait(), notify() 操作的目的是基于某种条件, 协调多个线程间的运行状态, 由于涉及到多个线程间基于共享变量的相互通信, 必然需要引入某种同步机制, 以确保wait(), notify() 操作在线程层面的原子性
    • 更多详细解析可参考文章

      为什么wait()和notify()需要搭配synchonized关键字使用

await() 和 signal() 的使用方式

  • wait() 和 notify() 方法是 Object 的方法, 而 await() 和 signal() 方法是接口 Condition 的方法, 官方文档对于 await() 和 signal() 有如下的说明。

Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations.

  • 翻译一下就是, Condition 这个接口把 Object 的

    wait(), notify(), notifyAll()

    分解到了不同的对象中, 搭配上任意一种 Lock 的使用, 使得一个对象可以拥有多个

    等待集
  • 这里有一个术语是

    waitset

    , 具体含义是

    • 每个对象都有一个等待集, 该等待集是一个线程的集合。

      这里写图片描述

    • wait(), notify(), notifyAll()

      方法的调用会对等待集中的线程进行移入或移除操作
  • 所以, await() 和 signall 的添加, 实际上是为我们提供了一种方便的基于同一个锁, 实现多个条件的 wait() 和 notify() 操作, 以一个有界缓冲区的生产消费操作为例(该例子实际上是

    ArrayBlockingQueue

    的简化版 , 感兴趣的读者可以直接阅读源码)

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   }
 }
  • 上面的例子中, 在一个 lock 控制的临界区中, 出现了两种条件(notFull, notEmpty)的操作。
  • 下面假设我们要通过 wait() 和 notify() 粗暴地进行替换
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     synchronized(lock)
     {
        while (count == items.length)
            lock.wait();
        items[putptr] = x;
        if (++putptr == items.length) putptr = 0;
        ++count;
        lock.notify();
     }
   }

   public Object take() throws InterruptedException {
     synchronized(lock){
       while (count == 0)
         lock.wait();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       lock.notify();
       return x;
     } 
   }
 }
  • 显然, 上述的代码仅仅在消费者和生产者分别只有一个时可以工作, 在有多个生产者和消费者时, 生产者A 可能会唤醒生产者 B 造成错误的结果, 为了避免这一错误。 则需要多个添加更多的对象, 使用

    嵌套

    的 synchronized 代码块。
class BoundedBuffer {
        final Lock lock = new ReentrantLock();
        final Condition notFull = lock.newCondition();
        final Condition notEmpty = lock.newCondition();

        final Object[] items = new Object[100];
        int putptr, takeptr, count;

        public void put(Object x) throws InterruptedException {
            synchronized (lock) {
                synchronized (notFull) {
                    while (count == items.length)
                        notFull.wait();
                }

                items[putptr] = x;
                if (++putptr == items.length) putptr = 0;
                ++count;
                synchronized (notEmpty) {
                    notEmpty.notify();
                }
            }
        }

        public Object take() throws InterruptedException {
            synchronized (lock) {
                synchronized (notEmpty)
                {
                    while (count == 0)
                        notEmpty.wait();
                }

                Object x = items[takeptr];
                if (++takeptr == items.length) takeptr = 0;
                --count;
                synchronized (notFull)
                {
                    notFull.notify();
                }
                return x;
            }
        }
    }
  • 显然, 代码的易用性和可读性都不如 Condition 方法的 await() 和 signal() 。

总结

await(), signal(),signalAll() 的功用和 wait(), notify(), notifyAll() 基本相同, 区别是, 基于 Condition 的 await(), signal(), signalAll() 使得我们可以在同一个锁的代码块内, 优雅地实现基于多个条件的线程间挂起与唤醒操作。



版权声明:本文为lengxiao1993原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。