Java阻塞队列

一、基本定义

        阻塞队列的顶级接口是BlockingQueue,它实现了Queue这个接口,官方对它的解释是:一个队列,它还支持在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用的操作,典型的应用场景有生产者-消费者、线程池和消息中间件等。

        BlockingQueue 为元素的操作有四种形式,用不同的方式处理不能立即满足,但可能在未来某个时间点满足的操作:

  • 抛出异常,当阻塞队列满时,再向队列中add添加元素会抛出IllegalStateException:Queue full;当阻塞队列空时,再向队列remove移除元素会抛出NoSuchElementException。
  • 插入方法,成功返回true,失败返回false移除方法,成功返回移出队列的元素,失败返回null。
  • 当阻塞队列满时,生产者线程继续往队列put元素,队列会一直阻塞生产者线程直到put数据或响应中断输出;当阻塞队列空时,消费者线程从队列take元素,队列会一直阻塞消费者线程直至队列中出现元素。
  • 当阻塞队列满时,队列会阻塞生产者线程一段时间,超过时限之后生产者线程自动退出 。

方法名如下表所示:

抛出异常返回特殊值无限期阻塞超时阻塞
插入add(e)offer(e)put(e)offer(e,time,unit)
移除removepoll()take()poll(time,unit)
检查element()peek()

        同时,Java为不同的业务场景还提供了不同的BlockingQueue实现类,具体如下:     

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按照FIFO(先进先出)的原则对元素进行排序;
  2. LinkedBlockingQueue:一个基于链表结构的有界(大小默认值为Integer.MAX_VALUE)阻塞队列,此队列按照FIFO(先进先出)的原则对元素进行排序,吞吐量通常要高于ArrayBlockingQueue。
  3. SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另外一个线程调用移除操作,否则插入操作一直处于阻塞状态。
  4. PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
  5. DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
  6. LinkedTransferQueue:由链表结构组成的无界阻塞队列
  7. LinkedBlockingDeque:由链表结构组成的双向阻塞队列

二、源码分析

        以ArrayBlockingQueue为例,它是基于数组结构的有界阻塞队列,具有以下特性:

  • 队列的头部是在队列中时间最长的元素,队列的尾部是在队列中时间最短的元素,新元素被插入到队列的尾部,队列检索操作获取队列头部的元素。
  • 同时也是一个经典的“有界缓冲区”,其中一个固定大小的数组保存由生产者插入并由消费者提取的元素,一旦创建,容量将无法更改。
  • 尝试将元素放入完整队列将导致操作阻塞;尝试从空队列中获取元素同样会阻塞。
  • 支持对等待的生产者和消费者线程进行排序的可选公平策略。默认情况下,采取非公平策略,可以通过改变构造器参数fair的值为true来实例化公平阻塞队列,公平性设置为 true 的队列以 FIFO 顺序授予线程访问权限。公平性通常会降低吞吐量,但会降低可变性并避免饥饿。

2.1 构造器

        ArrayBlockingQueue共包含3个构造器:

        1. 指定底层数组容量,该方法中调用了两参构造器,默认创建为非公平阻塞队列,如果capacity小于1,会抛出IllegalArgumentException异常,

public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

        2. 指定底层数组数量以及是否采用公平策略,初始化put和take方法中需要用到的关键成员变量:同步锁lock、非空条件队列notEmpty以及未满条件队列notFull;如果capacity小于1,会抛出IllegalArgumentException异常,

    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();
    }

        3. 指定底层数组数量、是否采用公平策略和初始元素集合,首先调用了两参构造器创建阻塞队列,然后上锁,按集合迭代器的遍历顺序向队列中添加元素。如果capacity小于c.size()或小于1,会抛出IllegalArgumentException异常,如果集合c或者集合c中的任意元素为空,抛出NullPointerException异常:

    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

2.2 元素操作方法

        1. add(E e):可以在不超过队列容量的情况下立即插入指定元素,则在此队列的尾部插入指定元素,成功时返回 true,如果此队列已满则抛出 IllegalStateException,实际底层通过调用offer方法来实现。

public boolean add(E e) {
        return super.add(e);
}

public boolean add(E e) {
   if (offer(e))
       return true;
   else
       throw new IllegalStateException("Queue full");
}

        2.  remove(Object o):循环遍历从队列中移除第一个等于o的元素,移除成功返回true,移除失败返回false

    public boolean remove(Object o) {
        if (o == null) return false;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count > 0) {
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
                    if (o.equals(items[i])) {
                        removeAt(i);
                        return true;
                    }
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

        3. element():使用peek方法检索队列的队头,如果不为空则返回该元素,并不会改变队列结构,如果为空则抛出NoSuchElementException

    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

        4. offer(E e):首先对元素进行判空,然后可以在不超过队列容量的情况下立即插入,则在此队列的尾部插入指定元素,成功时返回 true,如果队列已满则返回 false。 相比 add方法更友善,后者只能通过抛出异常来表明无法插入元素。

    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

        5. poll():移除队首元素并返回

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }

        6. peek(): 检索队列头部元素并返回该元素,并不改变队列结构,如果队列为空则返回null

    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }

        7. put(E e):在队列的尾部插入指定元素,如果队列已满,则等待队列空间可用,通过调用未满条件队列的await方法实现,等待被唤醒

    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 {
            lock.unlock();
        }
    }

        8. take():移除队首元素,如果队列为空,则等待队列中有元素,通过调用非空条件队列的await方法实现,等待被唤醒

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

        9. offer(E e, long timeout, TimeUnit unit):在offer(e)的基础上加了一个超时等待时间,向队尾插入元素,若队列已满,则阻塞指定时间,若此期间队列可插入则插入元素返回true,超时则返回false

    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

        10. poll(long timeout, TimeUnit unit):在poll方法的基础上加了一个超时等待时间,移除队首元素,若队列为空,则阻塞指定时间,若此期间队列不为空则移除队首元素返回true,超时则返回false

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

         11. enqueue(E x):元素入队列的关键方法,在线程持有锁时调用,向队尾插入元素,并唤醒非空条件队列notEmpty中等待的线程

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

        12. dequeue():元素出队耳朵关键方法, 在线程持有锁时调用,移除队首元素,并唤醒未满条件队列notFull中等待的线程

    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

         13. contains(Object o):如果队列包含至少一个o元素,返回true,否则返回false

    public boolean contains(Object o) {
        if (o == null) return false;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count > 0) {
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
                    if (o.equals(items[i]))
                        return true;
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值