juc4-BlockingQueue

一 类继承图

BlockingQueue.png

二 ArrayBlockingQueue

2.1 属性

//数组存储对象
final Object[] items;
//取出队数据的索引
int takeIndex;
//入队数据的存储索引
int putIndex;
//队列元素数量
int count;
//并发控制
final ReentrantLock lock;
//lock的条件对象,take()时若队列为空,则等待该条件对象
private final Condition notEmpty;
//lock的条件对象,put()时若队列为满,则等待该条件对象
private final Condition notFull;
/**
 * Shared state for currently active iterators, or null if there
 * are known not to be any.  Allows queue operations to update
 * iterator state.
 */
transient Itrs itrs = null;

2.2 实例化

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];//初始化存储数组
    lock = new ReentrantLock(fair);//初始化可重入锁
    notEmpty = lock.newCondition();//初始化条件对象
    notFull =  lock.newCondition();
}

public ArrayBlockingQueue(int capacity, boolean fair,
                          Collection c) {
    this(capacity, fair);

    final ReentrantLock lock = this.lock;
    lock.lock(); // Lock only for visibility, not mutual exclusion
    try {//加锁后,插入数据
        int i = 0;
        try {
            for (E e : c) {
                checkNotNull(e);
                items[i++] = e;
            }
        } catch (ArrayIndexOutOfBoundsException ex) {//不可超多配置的queue容量
            throw new IllegalArgumentException();
        }
        count = i;
        putIndex = (i == capacity) ? 0 : i;//更新入队索引,达到数组长度则从0开始
    } finally {
        lock.unlock();
    }
}

2.3 入队

  • offer(),成功返回true,失败返回false
public boolean offer(E e) {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)//先检查容量,
            return false;
        else {//入队
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;//数组存储数据
    if (++putIndex == items.length)//更新入队索引,达到存储数组长度则修改为0
        putIndex = 0;
    count++;
    notEmpty.signal();//通知调用take(),因无队列数据休眠等待获取出队数据的线程唤醒。
}
  • add() 成功返回true,失败抛异常
public boolean add(E e) {
    return super.add(e);
}

public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}
  • put()成功返回true,队列存储已满,则等待条件对象notFull通知
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();//可中断锁
    try {
        while (count == items.length)
            notFull.await();//存储满,休眠等待
        enqueue(e);//入队
    } finally {
        lock.unlock();
    }
}
  • 存储队列满时,指定等待超时时间。超时后仍无空间,返回false。成功返回true
public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

    checkNotNull(e);
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length) {
            if (nanos <= 0)//超时后仍无空间,返回false
                return false;
             //指定超时时间休眠等待
            nanos = notFull.awaitNanos(nanos);
        }
        enqueue(e);//入队
        return true;
    } finally {
        lock.unlock();
    }
}

2.4 获取出队数据

  • poll(),无数据返回null,有数据则获取出队数据
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];//获取当前takeIndex的数据
    items[takeIndex] = null;//清空引用
    if (++takeIndex == items.length)//下一出队数据索引+1,达到长度则循环赋值为0
        takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();//通知休眠等待入队的put(),线程唤醒获取锁后,执行入队操作。
    return x;
}
  • remove()获取出队数据,无数据抛异常。
public E remove() {
    E x = poll();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}
  • peek()仅获取下一出队数据,不做出队操作
public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return itemAt(takeIndex); // null when queue is empty
    } finally {
        lock.unlock();
    }
}
  • take()获取出队数据,无数据则休眠等待
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}
  • poll(long timeout, TimeUnit unit)获取出队数据,若无数据则按参数休眠指定时间。超时仍无数据则返回null
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0) {
            if (nanos <= 0)
                return null;
            nanos = notEmpty.awaitNanos(nanos);
        }
        return dequeue();
    } finally {
        lock.unlock();
    }
}

2.5 其他

  • 删除指定对象
public boolean remove(Object o) {
    if (o == null) return false;
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count > 0) {
//遍历队列,比较后删除
            final int putIndex = this.putIndex;
            int i = takeIndex;
            do {
                if (o.equals(items[i])) {
                    removeAt(i);//删除后,需要后面数据前移一位。若刚好为下一出队数据,则修改出队数据索引takeIndex+1。
                    return true;
                }
                if (++i == items.length)
                    i = 0;
            } while (i != putIndex);
        }
        return false;
    } finally {
        lock.unlock();
    }
}
  • 数组化,
public Object[] toArray() {
    Object[] a;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final int count = this.count;
        a = new Object[count];
        int n = items.length - takeIndex;
        if (count <= n)//数据连续分布在数据中,直接拷贝
            System.arraycopy(items, takeIndex, a, 0, count);
        else {//数据分布在数组两端,则分别拷贝到新数组中
            System.arraycopy(items, takeIndex, a, 0, n);
            System.arraycopy(items, 0, a, n, count - n);
        }
    } finally {
        lock.unlock();
    }
    return a;
}

三 LinkedBlockingQueue

3.1 属性

  • 使用单链表存储数据
static class Node {
    E item;
    Node next;
    Node(E x) { item = x; }
}
//头尾指针
transient Node head;
private transient Node last;
  • 出队入队分别使用单独的锁
/** 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();

3.2 入队/出队

  • 通知对应的条件对象,通知时需要加锁,所以调用通知函数前必须先释放入队出队的锁。避免死锁
/**
 * Signals a waiting take. Called only from put/offer (which do not
 * otherwise ordinarily lock takeLock.)
 */
private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}

/**
 * Signals a waiting put. Called only from take/poll.
 */
private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        notFull.signal();
    } finally {
        putLock.unlock();
    }
}
  • 入队出队基本流程和ArrayBlockingQueue一致,只是因使用两个锁,所以为避免死锁,通知条件对象的调用需要在释放锁之后执行。以put()代码为例:
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 node = new Node(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();
    }
    if (c == 0)//释放入队锁之后,再通知notEmpty
        signalNotEmpty();
}

四 LinkedBlockingDeque

  • 使用双链表存储数据,双向可入队出队数据
/** Doubly-linked list node class */
static final class Node {
    E item;
    Node prev;
    Node next;
    Node(E x) {
        item = x;
    }
}
  • 使用一个锁控制入队,出队,队列满通知,队列空通知
/** Main lock guarding all access */
final ReentrantLock lock = new ReentrantLock();

/** Condition for waiting takes */
private final Condition notEmpty = lock.newCondition();

/** Condition for waiting puts */
private final Condition notFull = lock.newCondition();
  • 入队,外部调用入队的逻辑和ArrayBlockingQueue一致
/**
 * Links node as first element, or returns false if full.
 */
private boolean linkFirst(Node node) {
    // assert lock.isHeldByCurrentThread();
    if (count >= capacity)
        return false;
    Node f = first;
    node.next = f;
    first = node;
    if (last == null)
        last = node;
    else
        f.prev = node;
    ++count;
    notEmpty.signal();
    return true;
}

/**
 * Links node as last element, or returns false if full.
 */
private boolean linkLast(Node node) {
    // assert lock.isHeldByCurrentThread();
    if (count >= capacity)
        return false;
    Node l = last;
    node.prev = l;
    last = node;
    if (first == null)
        first = node;
    else
        l.next = node;
    ++count;
    notEmpty.signal();
    return true;
}

  • 出队,,外部调用出队的逻辑和ArrayBlockingQueue一致
//头节点出队
private E unlinkFirst() {
    // assert lock.isHeldByCurrentThread();
    Node f = first;
    if (f == null)
        return null;
    Node n = f.next;
    E item = f.item;
    f.item = null;
    f.next = f; // help GC
    first = n;
    if (n == null)
        last = null;
    else
        n.prev = null;
    --count;
    notFull.signal();
    return item;
}

//尾节点出队
private E unlinkLast() {
    // assert lock.isHeldByCurrentThread();
    Node l = last;
    if (l == null)
        return null;
    Node p = l.prev;
    E item = l.item;
    l.item = null;
    l.prev = l; // help GC
    last = p;
    if (p == null)
        first = null;
    else
        p.next = null;
    --count;
    notFull.signal();
    return item;
}

你可能感兴趣的:(juc4-BlockingQueue)