您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

LinkedBlockingQueue源码解析

bubuko 2022/1/25 20:10:00 其他 字数 8188 阅读 999 来源 http://www.bubuko.com/infolist-5-1.html

LinkedBlockingQueue LinkedBlockingQueue是一个可以指定容量大小的以单链表为数据结构实现的队列,默认容量大小为 ,可以在构造方法中传入容量大小。 内部持有两个锁:读锁 ,控制元素的移除,对应条件 ,如果队列空,则阻塞线程; 写锁 ,控制元素的新增,对应条件 ,如果 ...

LinkedBlockingQueue

LinkedBlockingQueue是一个可以指定容量大小的以单链表为数据结构实现的队列,默认容量大小为Integer.MAX_VALUE,可以在构造方法中传入容量大小。

内部持有两个锁:读锁taskLock,控制元素的移除,对应条件notEmpty,如果队列空,则阻塞线程;

写锁putLock,控制元素的新增,对应条件notFull,如果队满,则阻塞线程。其实都是写锁,暂且如此称呼,作为区分。

当队列元素数量count达到队列容量大小capacity,则说明队满,count=0队空。

链表内部类

/**
 * 单链表
 */
static class Node<E> {
    E item;

    /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
    Node<E> next;

    Node(E x) { item = x; }
}

关键属性

// 队列容量
private final int capacity;
// 实际元素总数, count <= 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();

构造方法

/**
 * 无参构造,默认容量为Integer.MAX_VALUE 无界队列
 */
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

/**
 * 指定容量大小
 */
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}

新增元素

/**
 * put新增元素
 * 1、如果队列满了,则在队满条件上等待,当队列不满条件满足,再次判断队列不满成立,向下执行
 * 2、队列不满,将新增元素入队,当前元素作为队列尾节点
 * 3、队列元素总数+1, 
 * 4、如果队列总数未满,唤醒在不满条件上等待的线程
 * 5、如果新增之前队列是空的,还需要唤醒在非空条件上等待的线程
 */
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    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);
        // 获取元素总数,并将元素总数+1
        // c = 新增后元素总数 - 1
        c = count.getAndIncrement();
        // 队列还未满,唤醒在队列满条件上等待的线程
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        // 释放写锁
        putLock.unlock();
    }
    // 如果队列新增元素前是空的,那么这里唤醒在队列空条件上等待的线程
    if (c == 0)
        signalNotEmpty();
}

// 入队节点,将新增节点作为尾节点的后驱节点,并更新新增节点为尾节点
private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node;
}
/**
 * offer新增元素
 * 1、如果检测到队满,直接返回
 * 2、队未满,完成元素添加后,再次判断队列是否已满,如果未满,唤醒在队不满条件上等待的线程
 * 3、如果添加元素前队列空,则唤醒在队不空条件上等待的线程
 */
public boolean offer(E e) {
    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);
            // 总元素数量+1
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        }
    } 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();
        // 元素个数减1
        c = count.getAndDecrement();
        // 队列中还有元素,唤醒在非空条件上等待的线程
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    // 如果移除元素前,队列是满的,还需要唤醒在队满条件上等待的线程
    if (c == capacity)
        signalNotFull();
    return x;
}

/**
 * 出队元素
 */
private E dequeue() {
    // assert takeLock.isHeldByCurrentThread();
    // assert head.item == null;
    // 队首
    Node<E> h = head;
    // 对一个队列元素
    Node<E> first = h.next;
    h.next = h; // help GC
    // 将第一个队列元素置为队头,元素值设为null
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}
/**
 * 队列空,返回null
 */
public E poll() {
    final AtomicInteger count = this.count;
    // 队列空,直接返回null
    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();
            // 元素总数减1
            c = count.getAndDecrement();
            // 队列中还有元素,唤醒在非空条件上等待的线程
            if (c > 1)
                notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    // 如果移除元素前,队列是满的,还需要唤醒在队满条件上等待的线程
    if (c == capacity)
        signalNotFull();
    return x;
}

获取队列头元素

/**
 * 仅是将队头元素返回,不会移除
 */
public E peek() {
    if (count.get() == 0)
        return null;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        // 返回队头元素,实际是第二元素
        Node<E> first = head.next;
        // 如果为null,表明队列中没有元素,直接返回null
        if (first == null)
            return null;
        // 返回元素
        else
            return first.item;
    } finally {
        takeLock.unlock();
    }
}

remove移除元素

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) {
            // 如果队列中有相等的,从队列链表中移除
            if (o.equals(p.item)) {
                unlink(p, trail);
                return true;
            }
        }
        return false;
    } finally {
        // 释放读锁和写锁
        fullyUnlock();
    }
}

void unlink(Node<E> p, Node<E> trail) {
    // assert isFullyLocked();
    // p.next is not changed, to allow iterators that are
    // traversing p to maintain their weak-consistency guarantee.
    p.item = null;
    trail.next = p.next;
    // 移除的是队尾元素,则修改队尾
    if (last == p)
        last = trail;
    // 如果移除元素前队满,则唤醒在队满条件上等待的线程
    if (count.getAndDecrement() == capacity)
        notFull.signal();
}

void fullyLock() {
    putLock.lock();
    takeLock.lock();
}

void fullyUnlock() {
    takeLock.unlock();
    putLock.unlock();
}

contains

public boolean contains(Object o) {
    if (o == null) return false;
    // 读锁和写锁都上锁
    fullyLock();
    try {
        // 从队列链表头部开始遍历,查找是否有相等的元素
        for (Node<E> p = head.next; p != null; p = p.next)
            // 存在相等的元素
            if (o.equals(p.item))
                return true;
        return false;
    } finally {
        // 释放读锁和写锁
        fullyUnlock();
    }
}

LinkedBlockingQueue源码解析

原文:https://www.cnblogs.com/QullLee/p/12369775.html


如果您也喜欢它,动动您的小指点个赞吧

除非注明,文章均由 laddyq.com 整理发布,欢迎转载。

转载请注明:
链接:http://laddyq.com
来源:laddyq.com
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


联系我
置顶