连LinkedBlockingQueue源码都没看过,我怎么敢给你offer?(下)

简介: 连LinkedBlockingQueue源码都没看过,我怎么敢给你offer?(下)

3 构造方法

3.1 无参

  • 默认为 Integer 的最大值
  • image.png

3.2 有参

  • 指定容量大小.链表头尾相等,节点值(item)都是 null
  • image.png
  • 已有集合数据
public LinkedBlockingQueue(Collection<? extends E> c) {
    this(Integer.MAX_VALUE);
    final ReentrantLock putLock = this.putLock;
    putLock.lock(); // Never contended, but necessary for visibility
    try {
        int n = 0;
        for (E e : c) {
            // 集合元素不能为 null
            if (e == null)
                throw new NullPointerException();
            // capacity 代表链表的大小,在这为 Integer.MAX_VALUE
            // 若集合大小 > IntegerMAX_VALUE,直接抛异常.
            if (n == capacity)
                throw new IllegalStateException("Queue full");
            enqueue(new Node<E>(e));
            ++n;
        }
        count.set(n);
    } finally {
        putLock.unlock();
    }
}

4 新增

以 offer 方法为例,将元素E添加到队列的末尾

public boolean offer(E e) { 
  if (e == null) throw new NullPointerException(); 
  // 如果“队列已满”,则返回false,表示插入失败。 
  final AtomicInteger count = this.count;
  if (count.get() == capacity) return false; 
  int c = -1; 
  // 新建“节点e” 
  Node<E> node = new Node(e); 
  final ReentrantLock putLock = this.putLock; 
  // 获取“插入锁putLock” 
  putLock.lock(); 
  try { 
    // 再次对“队列是不是满”的进行判断。 
    // 若“队列未满”,则插入节点。 
    if (count.get() < capacity) { 
      // 插入节点 enqueue(node);
      // 将“当前节点数量”+1,并返回“原始的数量” 
      c = count.getAndIncrement(); 
      // 如果在插入元素之后,队列仍然未满,则唤醒notFull上的等待线程。 
      if (c + 1 < capacity)  notFull.signal(); 
    } 
  } finally { 
      // 释放“插入锁putLock” 
      putLock.unlock();
  } 
  // 如果在插入节点前,队列为空;则插入节点后,唤醒notEmpty上的等待线程 
  if (c == 0) 
    signalNotEmpty(); 
    return c >= 0;
  }

enqueue

  • 将node添加到队列末尾,并设置node为新的尾节点image.png

signalNotEmpty

  • 发出等待信号。 仅能从put/offer调用(否则通常不锁定takeLock),唤醒notEmpty上的等待线程.
  • image.png
  • 新增数据成功后,在适当时机,会唤起 put 的等待线程(队列不满时),或者 take 的等待线程(队列不为空时),这样保证队列一旦满足 put 或者 take 条件时,立马就能唤起阻塞线程,继续运行,保证了唤起的时机不被浪费。

5 取出

take

以take()为例,取出并返回队列的头。若队列为空,则一直等待。

public E take() throws InterruptedException { 
  E x; 
  int c = -1; 
  final AtomicInteger count = this.count; 
  final ReentrantLock takeLock = this.takeLock; 
  // 获取“取出锁”,若当前线程是中断状态,则抛InterruptedException 
  takeLock.lockInterruptibly(); 
  try { 
    // 若“队列为空”,则一直等待。 
    while (count.get() == 0) { 
      notEmpty.await(); 
    } 
    // 取元素 
    x = dequeue(); 
    // 取出元素之后,将“节点数量”-1;并返回“原始的节点数量”。 
    c = count.getAndDecrement();
    if (c > 1) notEmpty.signal();
  } finally { 
    // 释放“取出锁” 
    takeLock.unlock(); 
  } 
  // 如果在“取出元素之前”,队列是满的;则在取出元素之后,唤醒notFull上的等待线程。 
  if (c == capacity) 
    signalNotFull(); 
  return x;
}

dequeue

  • 删除队列的头节点,并将表头指向“原头节点的下一个节点”。
  • image.png

signalNotFull

  • 发出等待的信号。 仅能从take/poll调用.唤醒notFull上的等待线程.
  • image.png
目录
相关文章
|
9月前
|
存储 安全 Java
【面试题精讲】ArrayBlockingQueue 和 LinkedBlockingQueue 区别
【面试题精讲】ArrayBlockingQueue 和 LinkedBlockingQueue 区别
|
9月前
|
存储 安全 Java
ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别?
ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别?
|
11月前
|
存储 缓存 安全
BlockingQueue阻塞队列原理以及实现
BlockingQueue阻塞队列原理以及实现
89 0
|
11月前
|
算法 NoSQL 前端开发
|
12月前
|
存储 安全 Java
LinkedBlockingQueue 原理
LinkedBlockingQueue 原理
|
缓存 安全 Java
JUC系列学习(四):线程池阻塞队列BlockingQueue及其相关实现ArrayBlockingQueue、LinkedBlockingQueue
线程池阻塞队列BlockingQueue及其相关实现ArrayBlockingQueue、LinkedBlockingQueue
|
安全 Java
LinkedBlockingQueue源码学习
采用线程池和阻塞队列实现生产/消费者模型。其中LinkedBlockingQueue是阻塞队列,同时线程安全,其特点: 采用链表数据结构Node的方式进行节点数据的记录, 同时其进行入队和出队的计数器采用原子性的AtomicInteger 其出队和入队采用采用两把锁,putLock和takeLock,同时进行删除的时候,采用fullLock 其与LinkedBlockingQueue相比,其可以无界可以有界,而ArrayBlockingQueue是有界的,同时实现的数据结构不通过,一个采用数组、一个采用链表,同时采用的锁的方式不同,ArrayBlockingQueue采用一把锁,没有对生产和消
41 0
LinkedBlockingQueue源码学习
|
存储 安全 Java
面试侃集合 | LinkedBlockingQueue篇
面试侃集合 | LinkedBlockingQueue篇
165 0
面试侃集合 | LinkedBlockingQueue篇