offer() 方法做了以下几件事情:
- 检查需要入队的元素是否为 null。
- 判断队列是否满了,满了就返回 false。
- 队列没有满,创建一个新的 Node 节点。
- putLock 锁加锁,不让其他线程操作队列。
- 当队列没有满队的时候,将元素入队并且将局部变量 c 设置为入队之前元素的数量,元素数量 + 1。
- 再次判断队列是否满了,如果队列中还有空位,则唤醒正在阻塞的入队线程。这些阻塞的线程来自 put()、offer(E e, long timeout, TimeUnit unit) 方法。
- 释放 putLock 锁
- 当入队之前是一个空队列的时候,调用 signalNotEmpty() 方法开启 takeLock 出队锁,将阻塞在 notEmpty 条件队列中的线程唤醒。
enqueue() 方法的源码比较简单,就是将 last 节点的 next 指向需要入队的元素,如下图所示。
add()
add() 方法调用的是 offer() 方法,它在队列满了的时候不是返回 false 而是抛出一个 Queue full
异常。
put()
put() 方法和 offer()、and() 的方法大致相同,不同的是对队列满了之后的操作,offer() 是直接返回 false,and() 是抛出异常,put() 则是将线程加入到 notFull 条件队列中阻塞入队线程。
offer(E e, long timeout, TimeUnit unit)
这是一个带超时时间的 offer() 方法。
这个方法是在一定时间内元素等待入队,就是在 timeout 时间内队列中有空余位置就将元素加入单向链表的队尾,如果在 timeout 时间内元素还没有入队,就返回 false。
入队总结
LinkedBlockingQueue 的入队一共有 offer()、add()、put()、offer(E e, long timeout, TimeUnit unit) 四种方法。这四种方法在队列满了之后的处理是不同的:
- offer() 方法在队列满了之后就返回 false。
- add() 方法在内部调用的是 offer() 方法,当队列满了就抛出
Queue full
异常。 - put() 方法在队列满了之后会将线程加入 notFull 中,等待被唤醒后加入队列。
- offer(E e, long timeout, TimeUnit unit) 方法在队列满了之后会等待一段 timeout 的时间,在这时间内入队就返回 true,在这段时间内未能入队就返回 false。
- 每个方法在入队完后都会唤醒在 notEmpty 队列中等待出队的线程。
出队
LinkedBlockingQueue 的出队也有几种不同的方法,它们对于空队列的处理方式各不相同。
poll()
poll() 方法在出队的时候做了以下几件事情:
- 先取出队列中元素的数量
- 队列的非空检查,当队列是空的,则返回 false。
- 初始化一个局部的元素变量。
- takeLock 出队锁加锁,不让其他线程操作队列的出队。
- 当队列中有元素的时候,将队列中的第一个元素弹出。
- 判断队列中是否还有元素,唤醒阻塞在 notEmpty 条件队列上的线程。
- takeLock 出队锁解锁
- 在原队列是满队的情况下,唤醒阻塞在 notFull 条件队列上的线程。
dequeue() 方法会将 LinkedBlockingQueue 中第一个元素取出。取的并不是 head 中的item,而是 head.next 中的 item。