05.JUC 集合

来源:互联网 发布:新编诸子集成知乎 编辑:程序博客网 时间:2024/06/05 00:15

基本概念

LinkedBlockingQueue 是一个用链表实现的有界阻塞队列

LinkedBlockingQueue 按照先进先出的原则对元素进行排序。

LinkedBlockingQueue 采用了双锁、双条件队列来提高读写效率。


内部构造

LinkedBlockingQueue 内部维护着一个单向链表,且链表头节点的值永远为空。如下图所示:

输入图片说明

下面来看它的构成:

  • Node ,节点
static class Node<E> {    E item;    Node<E> next;    Node(E x) {        item = x;    }}
  • 构造函数
private transient Node<E> head;private transient Node<E> last;private final int capacity;public LinkedBlockingQueue() {    // 该队列是有界的,若不指定容量,默认为最大值    this(Integer.MAX_VALUE);}public LinkedBlockingQueue(int capacity) {    if (capacity <= 0){        // 抛出异常...    }    this.capacity = capacity;    last = head = new Node<E>(null);}

双锁机制

由于采用了双锁机制,因此它的出队、入队操作可以同时进行,从而提高效率。

// 用于入队操作private final ReentrantLock putLock = new ReentrantLock();private final Condition notFull = putLock.newCondition();// 用于出队操作private final ReentrantLock takeLock = new ReentrantLock();private final Condition notEmpty = takeLock.newCondition();

由于出入队可以同时进行,因此必须避免冲突问题。

  • 同一元素操作冲突:由于 LinkedBlockingQueue 采用了 FIFO(先进先出)的原则,实际上是双端操作,不会存在冲突。并且在其内部定义了两个变量:head、last。
// 出队修改头节点private transient Node<E> head;// 入队修改尾节点private transient Node<E> last;
  • 元素数量操作冲突:因为入队数量要+1,出队数量要-1,因此需要保证它的可见性,这里采用了这里采用原子类来实现:
private final AtomicInteger count = new AtomicInteger(0);

入队操作

  • offer,该操作成功返回 true,失败返回 false。
public boolean offer(E e) {    if (e == null){        // 抛出异常...    }    // 判断元素的个数是否超过容量4    final AtomicInteger count = this.count;    if (count.get() == capacity){        return false;    }    int c = -1;    Node<E> node = new Node(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();    }    // 为 0 表示之前队列是空的,唤醒出队时因为空队列而进入 notEmpty 条件等待队列的线程    if (c == 0){        signalNotEmpty();    }    return c >= 0;}
  • put,该操作成功返回 true,失败则进入阻塞。
 private final AtomicInteger count = new AtomicInteger(0);public void put(E e) throws InterruptedException {    if (e == null){        // 抛出异常...    }    int c = -1;    Node<E> 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){        signalNotEmpty();    }}
  • 关键
private void enqueue(Node<E> node) {    last = last.next = node;}private void signalNotEmpty() {    final ReentrantLock takeLock = this.takeLock;    takeLock.lock();    try {        notEmpty.signal();    } finally {        takeLock.unlock();    }}
  • 入队操作的过程如下所示:

输入图片说明


出队操作

  • poll,成功返回被移除的元素,失败返回 null。
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();    }    // 表示出队前满队列,唤醒因入队时满队列而进入 notFull 条件等待队列的线程。    if (c == capacity){        signalNotFull();    }    return x;}
  • take,成功返回被移除的元素,失败则线程阻塞。
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;}
  • 关键
private E dequeue() {    // 头节点、以及它的后继节点    Node<E> h = head;    Node<E> first = h.next;    // 等于 h.next = null,即断开后指针    h.next = h;     // 设置新的头节点    head = first;    E x = first.item;    // 将节点的值置空    first.item = null;    return x;}private void signalNotFull() {    final ReentrantLock putLock = this.putLock;    putLock.lock();    try {        notFull.signal();    } finally {        putLock.unlock();    }}
  • 出队过程如下图所示:

输入图片说明


0 0
原创粉丝点击