Condition接口对标 wait—notify等待通知机制

1 Condition接口

任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。

Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现 等待/通知 模式:

Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

public void conditionWait() throws InterruptedException {
    lock.lock();
    try {
              condition.await();
    } finally {
              lock.unlock();
    }
}

public void conditionSignal() throws InterruptedException {
    lock.lock();
    try {
              condition.signal();
    } finally {
              lock.unlock();
    }
}

当调用await()方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从await()方法返回,并且在返回前已经获取了锁。

常用方法:
在这里插入图片描述

2 Condition的实现:

ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个等待队列,该队列是Condition对象实现等待/通知功能的关键。

  • 当调用await()方法时,相当于同步队列(当唤醒后,就会在同步队列中竞争锁)的首节点(获取了锁的节点)移动到Condition的等待队列中。
    在这里插入图片描述

  • 调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点), 在唤醒节点之前,会将节点移到同步队列中。
    在这里插入图片描述

3 Condition与 wait—notify的区别与联系:

联系:
(1)有一组类似的方法:

  • 对象监视器: Object.wait()、Object.wait(long timeout)、Object.notify()、Object.notifyAll()。
  • Condition对象: Condition.await()、Condition.awaitNanos(long
    nanosTimeout)、Condition.signal()、Condition.signalAll()。

(2)都需要和锁进行关联:

  • 对象监视器: 需要进入synchronized语句块(进入对象监视器)才能调用对象监视器的方法。
  • Condition对象:需要和一个Lock绑定。

区别:
(1)Condition有拓展的方法:

  • awaitUninterruptibly():等待时忽略中断
  • awaitUntil(Date deadline) throws InterruptedException:等待到特定日期

(2)使用不同:

  • 对象监视器: 进入synchronized语句块(进入对象监视器)后调用Object.wait()。
  • Condition对象: 需要和一个Lock绑定,并显示的调用lock()获取锁,然后调用 Condition.await()。

(3)等待队列数量:

  • 对象监视器: 1个。
  • Condition对象: 多个。通过多次调用lock.newCondition()返回多个等待队列。 Condition可以有一个同步队列,多个等待队列。
    在这里插入图片描述
    双队列实例:
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)
                notFull.await();
            items[putptr] = x;
            if (++putptr == items.length)
                putptr = 0;
            ++count;
            // 因为放入了一个数据,数组肯定不是空的了
            // 此时唤醒等待这notEmpty条件上的线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    // 消费者方法,从数组里面拿数据
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0)
                // 数组是空的,没有数据可拿时,挂起等待,直到数组非空(notEmpty)
                notEmpty.await();
            Object x = items[takeptr];
            if (++takeptr == items.length)
                takeptr = 0;
            --count;
            // 因为拿出了一个数据,数组肯定不是满的了
            // 此时唤醒等待这notFull条件上的线程
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

(4)Condition可以唤醒特定线程

notify唤醒一个线程是随机的,而condition能做到唤醒特定的一个线程(在等待队列中等待时间最长的节点)。

注意:
同步队列和等待队列:

  • 一个线程调用对象的object.wait()方法后会进入等待队列,调用notifyaAll()方法会唤醒所有等待队列中的线程,进入同步队列里竞争锁,调用notify会随机唤醒一个等待队列中的线程进入同步队列,这个是随机的,总之需要区分等待队列与同步队列。只有同步队列里的线程有资格获得锁。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值