Java8 - LinkedBlockingQueue源码

一、概述

LinkedBlockingQueue 是一个基于单链表的无界阻塞队列,它跟 ArrayBlockingQueue 一样都是通过使用 ReentrantLock 来保证线程安全的。

LinkedBlockingQueue 不允许null值,也不强制我们指定队列得初始容量,默认的容量为 Integer.MAX_VALUE。

二、属性

//链表节点的结构,从这个类我们可以看出,它采用的是单链表结构
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();

三、方法

1、基本方法

下面的代码是入队和出队操作的实现,我们可以看到,LinkedBlockingQueue 通过一个队头指针和一个队尾指针来保证入队和出队的复杂度都为O(1)。

//入队
private void enqueue(Node<E> node) {
    last = last.next = node;
}
//出队
private E dequeue() {
    Node<E> h = head;
    Node<E> first = h.next;
    h.next = h; // help GC
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}

跟 ArrayBlockingQueue 一样,LinkedBlockingQueue 也提供了删除指定节点的操作来满足删除指定对象的需求,源码如下:

//删除节点p
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();
}

2、入队操作

LinkedBlockingQueue 提供了多种入队操作的实现来满足不同情况下的需求,入队操作有如下几种:

public boolean add(E e);
public void put(E e) throws InterruptedException;
public boolean offer(E e);
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;

下面是几种入队操作的具体实现,从源码中我们可以发现,这几种入队操作都直接或间接地调用了enqueue方法,所以只要搞清楚了enqueue方法,就已经没有什么难点了。这几种入队操作的区别如下:

  • add(E e):入队成功返回true,入队失败抛出 IllegalStateException 异常。
  • put(E e):如果队列满了,会一直等待,直到成功入队。
  • offer(E e):入队成功返回true,入队失败返回false。
  • offer(E e, long timeout, TimeUnit unit):在指定的时间内一直尝试入队,入队成功返回true,超时返回false。
public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

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 {
        //如果队列满了,则进行等待并释放putLock锁
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

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);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}

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

3、出队操作

同时,LinkedBlockingQueue 也提供了多种出队操作的实现来满足不同情况下的需求,如下:

public E poll();
public E poll(long timeout, TimeUnit unit) throws InterruptedException;
public E take() throws InterruptedException;

下面是几种出队操作的具体实现,与入队操作类似,这几种出队操作都直接或间接地调用了dequeue方法,所以如果搞清楚了前面的dequeue方法,就已经没有什么难点了。这几种出队操作的区别如下:

  • take():如果队列为空,就进行等待,直到出队成功,并返回队头元素。
  • poll():如果队列不为空,则出队并返回队头元素,否则返回null。
  • poll(long timeout, TimeUnit unit):在指定的时间内一直尝试出队,出队成功返回队头元素,否则返回null。
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;
}

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

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

4、其他方法

//只获取队头元素,不出队,如果队列为空,则返回null
public E peek() {
    if (count.get() == 0)
        return null;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        Node<E> first = head.next;
        if (first == null)
            return null;
        else
            return first.item;
    } finally {
        takeLock.unlock();
    }
}
//从队列中删除指定的对象
//删除操作最坏情况下会遍历整个链表,所以复杂度为O(n)
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();
    }
}
//判断队列中是否存在该元素
//该操作最坏情况下会遍历整个链表,所以复杂度为O(n)
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();
    }
}
//获取队列中元素的个数
public int size() {
    return count.get();
}
//获取队列中剩余的容量
public int remainingCapacity() {
    return capacity - count.get();
}
//清空队列,复杂度为O(n)
public void clear() {
    fullyLock();
    try {
        for (Node<E> p, h = head; (p = h.next) != null; h = p) {
            h.next = h;
            p.item = null;
        }
        head = last;
        // assert head.item == null && head.next == null;
        if (count.getAndSet(0) == capacity)
            notFull.signal();
    } finally {
        fullyUnlock();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值