Condition的await和signal原理详解(Condition下的生产消费模型)

本文详细解析了Java中Condition的await和signal方法,对比了它们与Object的wait和notify的区别。介绍了Condition的等待队列管理、节点插入以及线程在等待队列和同步队列之间的转移过程。此外,还展示了Condition在实现有界队列和生产消费者模型中的应用。
摘要由CSDN通过智能技术生成

Condition的await和signal与Object的wait与notify区别:

任何一个Java对象都天然继承于Object类,在线程间实现通信会用到Object的几个方法,如wait(),wait(long timeout),wait(long timeout,int nanos)与notify(),notifyAll()这几个方法实现等待通知机制,同样的,在Java Lock体系有同样的方法实现等待通知机制。从整体上看Object的wait与notify是与对象监视器(synchronized同步代码块或者同步方法中)配合完成线程间的等待通知机制,而Condition的await和signal与Lock配合完成等待通知机制,前者是JVM底层级别(不可以看源码),后者是Java语言级别,具有更高的可控制性和扩展性(可以看源码)。
两者在功能特性上还有如下不同:
1.Condition支持不响应中断,而Object不支持,也就是Object只要有中断就要响应。
2.Condition支持多个等待队列(new多个Condition对象),而Object只有一个等待队列,但两者都只要一个同步队列;
3.Condition支持截止时间设置,而Object是超时时间设置,支持截止时间设置,不用计算需要等多久。

Condition :

public interface Lock {
   
   Condition newCondition();
   }
public interface Condition {
   
	//当前线程进入等待状态,直到被中断或唤醒
	 void await() throws InterruptedException;

	//不响应中断(即使有中断,也不会抛异常),直到被唤醒
	 void awaitUninterruptibly();
	 
	 //当前线程进入等待状态直到被通知,中断或者超时;
	 long awaitNanos(long nanosTimeout) throws InterruptedException;

	//同Object.wait(long timeout),多了自定义时间单位
	//直到超时,中断,被唤醒
	 boolean await(long time, TimeUnit unit) throws InterruptedException;
	 //支持设置截止时间,直到到截止时间、中断、被唤醒
	 boolean awaitUntil(Date deadline) throws InterruptedException;

	//唤醒一个等待在Condition(等待队列)上的线程,将该线程由等待队列转移到同步队列
	 void signal();
    //将所有等待在condition上的线程全部转移到同步队列中
	 void signalAll();

Condition实现原理分析:


等待队列

创建一个Condition对象是通过lock.newCondition( ),而这个方法实际上会new出一个ConditionObject对象,该类是AQS的一个内部类(lock的实现原理依赖AQS)。

 public class ConditionObject implements Condition, java.io.Serializable {
   
 		/** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
}

在ConditionObject 通过持有等待队列的头尾指针来管理等待队列。这个Node复用了AQS的Node类,也就是等待队列和同步队列的结点一个Node类。
Node类中有这样一个属性:

//后继节点
Node nextWaiter;

nextWaiter是等待队列中标识下一个结点,也就是说Node结点的prev和next等待队列没有用到。
等待队列是一个单向队列,而同步队列是一个双向队列。

package CODE.多线程;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Await {
   
    public static void main(String[] args) {
   
        Lock lock=new ReentrantLock();
        Condition condition=lock.newCondition();
        for(int i=0;i<10;i++)
        {
   
            Thread thread=new Thread(()->
            {
   
                lock.lock();
                try {
   
                    condition.await();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }finally {
   
                    lock.unlock();
                }
            });
            thread.start();
        }
    }
}

在这里插入图片描述
Debug模式下可以看见:
1.调用condition.wait方法后线程依次尾插到等待队列中,对上例来说,顺序依次是Thread-0,Thread-1,Thread-2…Thread-9;
2.等待你队列是一个单向队列,只有nextWaiter。
由于condition是new出来的,也就是说当多次调用lock.newCondition()可以创建多个condition对象,也就是一个lock可以有多个等待队列。
用Object类wait在Object对象监视器上只有一个同步队列和一个等待队列,而在并发包中的Lock中有一个同步队列多个等待队列。

在这里插入图片描述

Condition的应用:实现有界队列
有界队列是一种特殊的队列,当队列为空时,队列的获取(删除)操作将会阻塞获取(删除)线程,直到队列中有新增结点;当队列已满时,队列的插入操作将会阻塞添加线程,直到队列出现空位。
代码如下:

package CODE.多线程;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//Conditon实现有界队列
class BoundQueue<T>
{
   
    private Object[] items;
    private int counts=0;  //intems中元素个数
    private Lock lock=new ReentrantLock();
    private Condition fullCondition=lock.newCondition();
    private Condition emptyCondition=lock.newCondition();
    public BoundQueue(int size)
    {
   
        items=new Object[size];
    }

    //向数组里添加元素,如果数组满,进入等待状态
    public void add(T t,int addIndex) throws InterruptedException {
   
        try
        {
   
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值