深入理解AQS的condition队列

系列文章目录


前言

上一篇讲了CLH队列,这一篇通过ArrayBlockingQueue的源码来讲以下condition队列

一、ArrayBlockingQueue的结构

先看一下构造方法

public ArrayBlockingQueue(int capacity, boolean fair) {
   
   
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

有一个数组items,一个可重入锁lock,两个Condition队列notEmpty和notFull

1. 数组

items:用于存放元素。


    /** The queued items */
    final Object[] items;

我们知道,ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,按FIFO排序任务。items就锁用于存放元素的数组,那FIFO怎么实现呢?通过下面两个指针来实现

takeIndex:下一个要获取的元素数组坐标位置
putIndex:下一个元素要放入的数组坐标位置


    /** items index for next take, poll, peek or remove */
    int takeIndex;

    /** items index for next put, offer, or add */
    int putIndex;

并且这两个指针是循环移动的,如果指针达到最大坐标了,又会从0开始

关于数组的还有一个重要的属性,那就是count
count:表示队列中元素的个数。ArrayBlockingQueue会有很多地方用到对count数量的判断,后面会讲到


    /** Number of elements in the queue */
    int count;

2.可重入锁

ArrayBlockingQueue的一个重要的特性是在任意时刻只有一个线程可以进行获取或者放入操作,所以就需要一个锁来实现,ArrayBlockingQueue的获取和放入操作都需要获取到这个lock才能进行


    /** Main lock guarding all access */
    final ReentrantLock lock;

3.condition队列的结构

在这里插入图片描述

condition队列是一个单向链表,在该链表中我们使用nextWaiter属性来串联链表。但是,就像在同步队列中不会使用nextWaiter属性来串联链表一样,在条件队列是中,也并不会用到prev, next属性,它们的值都为null。

ArrayBlockingQueue中定义了2个condition队列,notEmpty和notFull
notEmpty:等待获取元素的condition队列。如果items数组空了,那么获取元素的线程就会进入该condition队列
notFull:等待放入元素的condition队列。如果items数组空了,那么放入元素的线程就会进入该condition队列


    /** Condition for waiting takes */
    private final Condition notEmpty;

    /** Condition for waiting puts */
    private final Condition notFull;

我们从构造方法中的lock.newCondition()看到,这个队列就是AQS中的ConditionObject


final ConditionObject newCondition() {
   
   
            return new ConditionObject();
        }

而ConditionObject有2个属性头节点指针和尾节点指针,如上面的图片显示

        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

二、ArrayBlockingQueue主要方法

1)public boolean add(E e):放入元素,放入成功了返回true,如果items满了,抛异常
2)public boolean offer(E e):放入元素,放入成功了返回true,如果items满了,返回false
3)public void put(E e):放入元素,如果items满了,阻塞
4)public boolean offer(E e, long timeout, TimeUnit unit):放入元素,放入成功了返回true,如果items满了,等待一段时间,超时后返回false
5)public E poll():获取元素,如果items空了,返回null
6)public E take():获取元素,如果items空了,阻塞
7)public E poll(long timeout, TimeUnit unit):获取元素,如果items空了,等待一段时间,超时后返回null

三、ArrayBlockingQueue源码解读

先看放入元素的几个方法,这几个的方法都比较相似,但是只有put(E e)和offer(E e, long timeout, TimeUnit unit)用到了条件队列,我们从这两个方法进行分析

1.put(E e)

public void put(E e) throws InterruptedException {
   
   
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
   
   
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
   
   
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值