java阻塞队列之LinkedBlockingQueue

LinkedBlockingQueue简介
①LinkedBlockingQueue底层数据结构单向链表
②获取元素按照FIFO原则
③头尾通过独立的锁来控制并发
④可以构建时指定容量,默认为Integer.MAX_VALUE,无界队列,使用时注意内存溢出问题!

​​​​LinkedBlockingQueue内部属性

// 链表结点结构
static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

private final int capacity; // 队列最大容量
private final AtomicInteger count = new AtomicInteger(); // 队列元素个数
transient Node<E> head; // 链表头结点
private transient Node<E> last; // 链表尾结点
// 头尾并发控制锁
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();

 入队方法

public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException(); // put元素非空
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly(); // 可中断
        try {
            while (count.get() == capacity) {
                notFull.await(); //当前元素已达最大容量阻塞在此
            }
            enqueue(node); // last = last.next = node;这做了两个操作,尾结点next指向node,将node置为last
            c = count.getAndIncrement();// 此处c是元素入队前队列中个数
            if (c + 1 < capacity) // 入队后队列仍没到最大容量,唤醒put阻塞线程
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0) // 该元素入队前队列为空(不包括head空节点),此时可能之前有已经被阻塞的take线程,唤醒take阻塞线程
            signalNotEmpty();
    }

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

        if (e == null) throw new NullPointerException(); // 元素非空
        long nanos = unit.toNanos(timeout);
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) { 
                if (nanos <= 0) // 与上述方法区别: 阻塞指定时间,到期后队列仍满,返回false
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(new Node<E>(e));
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0) 
            signalNotEmpty();
        return true; // 添加成功返回值true
    }

    public boolean offer(E e) { // 该方法添加失败,直接返回false
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity) // 队列元素已满
            return false;
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
                enqueue(node); // 队列未满则入队
                c = count.getAndIncrement();
                if (c + 1 < capacity)
                    notFull.signal(); // 入队后容量未达最大值,唤醒阻塞的put线程
            }
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }

出队方法

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) // 出队前队列已满,那么可能存在被阻塞的put线程。唤醒一次
            signalNotFull();
        return x;
    }

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        int c = -1;
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

    public E poll() {
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            return null;
        E x = null;
        int c = -1;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            if (count.get() > 0) {
                x = dequeue();
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

peek获取第一个节点(不包含head结点)

    public E peek() {
        if (count.get() == 0) // 队列为空,返回空
            return null;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            Node<E> first = head.next; // peek获取的是head后的第一个节点
            if (first == null)
                return null;
            else
                return first.item;
        } finally {
            takeLock.unlock();
        }
    }

unlink(Node<E> p, Node<E> trail)移除指定节点
参数p,待移除节点;参数trail为p的前继节点

void unlink(Node<E> p, Node<E> trail) {
        p.item = null; // 置空p的item
        trail.next = p.next; // p的前继结点指向p的后继结点
        if (last == p) 
            last = trail; // 如果p是尾结点,则trail为last尾结点
        if (count.getAndDecrement() == capacity)
            notFull.signal(); // 队列元素个数到达最大容量,唤醒take消费线程
    }

remove移除指定item值的节点

public boolean remove(Object o) {
        if (o == null) return false;
        fullyLock();
        try {
            for (Node<E> trail = head, p = trail.next;
                 p != null;
                 trail = p, p = p.next) {// 每次trail都记录前继结点
                if (o.equals(p.item)) {
                    unlink(p, trail);
                    return true;
                }
            }
            return false;
        } finally {
            fullyUnlock();
        }
    }

LinkedBlockingQueue迭代器:Iterator和LBQSpliterator
Iterator在LinkedBlockingQueue中描述过,简单就不说了。主要描述下LBQSpliterator的源码。
可拆分迭代器的 tryAdvance、forEachRemaining、trySplit方法都是需要获取fullLock的,所以注意对吞吐量的影响,tryAdvance获取第一个item不为空的节点数据做指定的操作,forEachRemaining循环遍历当前迭代器中所有没有被移除的节点(item不为空)做指定的操作。

        public Spliterator<E> trySplit() {
            Node<E> h;
            final LinkedBlockingQueue<E> q = this.queue;
            int b = batch; // 初始0
            int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1; // 从1开始,不断累加,直到最大1 << 25
            if (!exhausted && // exhausted用于判断遍历的结点是否已到尾结点
                ((h = current) != null || (h = q.head.next) != null) &&
                h.next != null) {
                Object[] a = new Object[n];
                int i = 0;
                Node<E> p = current;
                q.fullyLock();
                try {
                    if (p != null || (p = q.head.next) != null) {
                        do {
                            if ((a[i] = p.item) != null) // 非空元素添加到数组a
                                ++i;
                        } while ((p = p.next) != null && i < n);
                    }
                } finally {
                    q.fullyUnlock();
                }
                if ((current = p) == null) { // current = p记录上一次遍历到的节点。如果为空,说明已经到达尾结点
                    est = 0L; // 队列中还剩多少元素未遍历,此处0表示遍历完了
                    exhausted = true;
                }
                else if ((est -= i) < 0L) // 队列中全部结点已遍历完了
                    est = 0L;
                if (i > 0) { 
                    batch = i; // 当前拆分的个数
                    return Spliterators.spliterator
                        (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
                         Spliterator.CONCURRENT);
// ORDERED有序、NONNULL非空、CONCURRENT表示可以同时修改源,即我们可以使用多个线程并发地添加,修改或删除元素,而无需同步。
                }
            }
            return null;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            if (action == null) throw new NullPointerException();
            final LinkedBlockingQueue<E> q = this.queue;
            if (!exhausted) {
                exhausted = true;
                Node<E> p = current;
                do {
                    E e = null;
                    q.fullyLock();
                    try {
                        if (p == null)
                            p = q.head.next;
                        while (p != null) {
                            e = p.item;
                            p = p.next;
                            if (e != null)
                                break;
                        }
                    } finally {
                        q.fullyUnlock();
                    }
                    if (e != null)
                        action.accept(e);
                } while (p != null);
            }
        }

        public boolean tryAdvance(Consumer<? super E> action) {
            if (action == null) throw new NullPointerException();
            final LinkedBlockingQueue<E> q = this.queue;
            if (!exhausted) {
                E e = null;
                q.fullyLock();
                try {
                    if (current == null)
                        current = q.head.next;
                    while (current != null) {
                        e = current.item;
                        current = current.next;
                        if (e != null)
                            break;
                    }
                } finally {
                    q.fullyUnlock();
                }
                if (current == null)
                    exhausted = true;
                if (e != null) {
                    action.accept(e);
                    return true;
                }
            }
            return false;
        }

LinkedBlockingQueue的迭代器拆分很特别,它不是像ArrayBlockingQueue那样每次分一半,而是第一次只拆一个元素,第二次拆2个,第三次拆三个,依次内推,拆分的次数越多,后面的迭代器分的得元素越多,直到一个很大的数MAX_BATCH ,后面的迭代器每次都分到这么多的元素,拆分的实现逻辑很简单,每一次拆分结束都记录下拆分到哪个元素,下一次拆分从上次结束的位置继续往下拆分,直到没有元素可拆分了返回null。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值