LinkedBlockingQueue

LinkedBlockingQueue是一个可选是否有界的阻塞队列,由链表实现。

同其他队列一样,它遵循先进先出(FIFO)规则,队头元素是队列中存储时间最长的元素,队尾是队列中存储时间最短的元素。元素从队头取出,从队尾插入。

该队列拥有比ArrayBlockingQueue更高的吞吐量,但是在高并发情况下的可预测性能较差。

LinkedBlockingQueue源码:

/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();

/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();

ArrayBlockingQueue源码:

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

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

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

由源码可见,LinkedBlockingQueue性能更高的一部分原因在于它使用的是分离锁,读和写是分离的,所以可以并发进行。

ArrayBlockingQueue使用的是公共锁,所以读时写阻塞,写时读阻塞。

该队列在创建时可以给定一个数值用于初始化容量,或默认使用Inteager.MAX作为初始容量

在向队列使用put()方法添加元素时,若容量超过最大容量,则会进行等待。使用offer(E e, long timeout, TimeUnit unit)添加元素时,若容量超限则会进行有限等待,若在给定时间内没有插入成功,则返回false

private final AtomicInteger count = new AtomicInteger();

底层实现使用AtomicInteger来存储队列中现有元素数,因为它是线程安全的。

1. 入队

put(E e)
/**
     * Inserts the specified element at the tail of this queue, waiting if
     * necessary for space to become available.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);   //创建一个新节点
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();   //获取锁
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {   //如果队列已满则阻塞该线程,将执行权让给其他生产者线程
                notFull.await();
            }
            enqueue(node);    //入队
            c = count.getAndIncrement();
            if (c + 1 < capacity)    //再次判断是否还有空位,如果有的话要唤醒其他生产者线程
                notFull.signal();
        } finally {
            putLock.unlock();   //解锁
        }
        if (c == 0)
            signalNotEmpty();
    }

与其他支持并发的Collection子类一样,LinkedBlockingQueue也不允许Null值。

在添加元素时首先获得锁,然后检查是否容量已满,若已满则阻塞该线程。若未满则入队,之后获取当前count再增加count值。此时c表示当前添加元素后的容量 - 1c + 1 < capacity用于判断添加元素后是否还有空间,若有的话唤醒其他等待的生产者线程。解锁后判断是否c == 0,由于c = 0表示添加前容量为0,则可知队列在该元素添加之前的状态是所有消费者都被阻塞,所以需要在这时候唤醒所有的消费者线程。

//入队源码
private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node;
}
take()
public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x; 
}

put方法类似。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值