JUC源码系列之LinkedBlockingQueue

LinkedBlockingQueue 介绍

基于单向链表实现的有界阻塞队列,遵循先进先出(FIFO)原则。可用于实现一个生产者消费者模型,由于内部使用了 takeLock 和 putLock 两把锁,还有notEmpty 和 notFull 两个条件变量,因此它可以做到读与写并行操作。使用 AtomicInteger 记录队列中元素的个数。

核心变量介绍

/** 队列的容量,默认为 int 的最大值 */
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();

核心方法介绍

take方法

从队列头部取数据

1、先获取读锁 takeLock

2、判断队列是否为空

3、取数据

4、原子性减少元素数量

5、唤醒操作

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(); // 队列中元素数量原子性减 1 ,并返回减一之前的原始大小值
        if (c > 1)
            notEmpty.signal(); // 队列不为空,唤醒其他消费者继续消费
    } finally {
        takeLock.unlock();
    }
    if (c == capacity) // 由于 c 是队列元素减一之前的原始大小值,从队列取出一个元素后,唤醒生产者继续生产数据
        signalNotFull();
    return x;
}

dequeue方法

private E dequeue() {
    Node<E> h = head;
    Node<E> first = h.next;
    h.next = h; // 将原始头节点的next 指向自己,将自己从队列中移除
    head = first; // 设置新的头节点
    E x = first.item;
    first.item = null;// 因为队列的头节点是一个伪节点(不保存数据的节点),所以将item置空
    return x;
}

signalNotFull 方法

private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        notFull.signal(); // 唤醒生产者
    } finally {
        putLock.unlock();
    }
}

put方法

向队列中插入数据

1、获取 putLock 锁,只有获取到锁才有操作队列的权限

2、判断队列是否已满,满了,阻塞等待

3、插入队列

4、插入成功后,如果队列没满,唤醒其它可能正在阻塞的生产线程

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);// 插入队列
        c = count.getAndIncrement();
        // 当前队列未满,唤醒其它生产者继续生产数据
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    // 因为 count 默认值为0 , 执行到此步判断时,说明生产者已经将数据插入队列了,需要唤醒正在等待的消费者
    if (c == 0)
        signalNotEmpty();
}
/** 将 node 节点插入队列尾部 */
private void enqueue(Node<E> node) {
    last = last.next = node;
}

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) {
    p.item = null;
    trail.next = p.next;
    if (last == p)
        last = trail;
    if (count.getAndDecrement() == capacity)
        notFull.signal();
}

drainTo方法

从队列中移除指定数向量的元素放到集合 c 中

public int drainTo(Collection<? super E> c, int maxElements) {
    boolean signalNotFull = false;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        int n = Math.min(maxElements, count.get());
        Node<E> h = head;
        int i = 0;
        try {
            while (i < n) {
                Node<E> p = h.next;
                c.add(p.item);
                p.item = null;
                h.next = h;
                h = p;
                ++i;
            }
            return n;
        } finally {
            if (i > 0) {
                head = h;// 更新头节点
                // 检测移动元素之前队列中元素是否已存满
                signalNotFull = (count.getAndAdd(-i) == capacity);
            }
        }
    } finally {
        takeLock.unlock();
        if (signalNotFull)
            signalNotFull();// 根据上述条件决定是否唤醒生产者
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值