文章目录
ArrayBlockingQueue
ArrayBlockingQueue 底层是采用数组实现的一个队列。一般称为有界队列
,其阻塞方式是基于 ReentrantLock 实现的,线程先在等待队列里面,signal() 将等待队列的首节点加入到阻塞队列的队尾,unlock() 后开始唤醒线程。
常用方法
-
存数据
- add(E e):添加数据到队列,满了抛异常
- offer(E e):添加数据到队列,满了返回false
- put(E e):添加数据到队列,满了线程挂起
- offer(E e, long timeout, TimeUnit unit):添加数据到队列,满了线程挂起一段时间
-
取数据
- remove():从队列拿数据,拿到返回,拿到null,抛异常
- poll():从队列拿数据,拿到返回,拿到null,也返回
- take():从队列拿数据,拿到返回,没拿到一直阻塞
- poll(long timeout, TimeUnit unit):从队列拿数据,拿到返回,没拿到阻塞一段时间
核心属性
/** 数组 */
final Object[] items;
/** 下次取数索引 */
int takeIndex;
/** 下次存放数据索引 */
int putIndex;
/** 数组长度 */
int count;
/** ReentrantLock 锁 */
final ReentrantLock lock;
/** 取值等待队列 */
private final Condition notEmpty;
/** 存值等待队列 */
private final Condition notFull;
源码解析
存数据
// 添加成功返回ture
// 添加失败抛异常
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
// 添加成功返回ture
// 添加失败返回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();
}
}
// 数组满了挂起线程
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();
}
}
// 数组满了挂起一段时间
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)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
重点看 enqueue() 即可
// 存放数据到数组中
private void enqueue(E x) {
// 拿到数组
final Object[] items = this.items;
// 在数组 putIndex 位置插入数据
items[putIndex] = x;
// 存放数据索引到最后一个位置就置为 0
if (++putIndex == items.length)
putIndex = 0;
// 数组长度 ++
count++;
// 唤醒取值的阻塞线程
notEmpty.signal();
}
取数据
// 没拿到数据抛异常
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
// 拿到null也返回
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
// 从队列拿数据,拿到返回,没拿到一直阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
// 从队列拿数据,拿到返回,没拿到阻塞一段时间
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();
}
}
重点看 dequeue() 即可
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
// 取数
E x = (E) items[takeIndex];
items[takeIndex] = null;
// 取数索引到数组长度置为 0
if (++takeIndex == items.length)
takeIndex = 0;
count--;
// 迭代器相关操作
if (itrs != null)
itrs.elementDequeued();
// 唤醒存值的阻塞线程
notFull.signal();
return x;
}
LinkedBlockingQueue
底层基于 Node 封装的单向链表实现,会将每个元素封装为 Node(LinkedBlockingQueue 的内部类)结点,Node 有当前值,next指针,一般称为无界队列
。内部提供了读锁和写锁,读写不互斥。
核心属性
// 阻塞队列元素封装为Node
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
// 队列长度 不传为 Integer.MAX
private final int capacity;
// 数据条数
private final AtomicInteger count = new AtomicInteger();
// 伪头结点 isNULL
transient Node<E> head;
// 伪尾结点 isNULL
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();
// 构造方法,将 last 与 head 给定 null
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
源码解析
存数据
// 添加成功返回ture
// 添加失败抛异常
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
// 添加到队列,满了返回false
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
// 队列满了返回false
if (count.get() == capacity)
return false;
int c = -1;
// 包装成 Node 结点
Node<E> node = new Node<E>(e);
// 写锁
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
// 插入数据
enqueue(node);
// count ++
c = count.getAndIncrement();
// 添加完数据之后,长度依然小于最大长度,唤醒可能阻塞的写线程
// 读写不互斥,可能前面在执行时,队列是满的,但是读操作依然在进行
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
// c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
if (c == 0)
signalNotEmpty();
return c >= 0;
}
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 {
// 队列满了,一直阻塞
while (count.get() == capacity) {
notFull.await();
}
// 存放数据
enqueue(node);
c = count.getAndIncrement();
// 添加完数据之后,长度依然小于最大长度,唤醒可能阻塞的写线程
// 读写不互斥,可能前面在执行时,队列是满的,但是读操作依然在进行
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
// c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
if (c == 0)
signalNotEmpty();
}
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();
}
// c == 0,说明添加数据之前,队列是空的,唤醒可能阻塞的读线程
if (c == 0)
signalNotEmpty();
return true;
}
重点看 enqueue() 即可
// 往尾部插入数据
private void enqueue(Node<E> node) {
last = last.next = node;
}
取数据
// 没拿到数据抛异常
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
//
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 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(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;
}
重点看 enqueue() 即可
// 从头开始取,first 指向 第二个值
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;
}
上面的 await() 与 signal() 调用了 AQS 中 ConditionObject 类的方法,这里不在赘述
SynchronousQueue
SynchronousQueue 传递数据,并不存储数据,队列的长度是 0 ,一个线程将数据丢到 SynchronousQueue 后,必须等另一个线程拿到这个数据,两者配对成功才可以。可作为线程间的通讯。
简单使用
public static void main(String[] args) throws InterruptedException {
SynchronousQueue queue = new SynchronousQueue();
new Thread(() -> {
User user = new User();
user.setName("jack&rose");
System.out.println("publisher:" + user);
queue.offer(user);
}).start();
new Thread(() -> {
try {
Object user = queue.take();
System.out.println("consumer:" + user);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
核心属性
SynchronousQueue 无论是消费者还是生产者,都用到了Transferer中的transfer方法
- 生产者调用transfer方法,需要传递一个参数,也就是数据
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
- 消费者调用transfer方法,第一个参数传递为null,代表获取数据
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = transferer.transfer(null, true, unit.toNanos(timeout));
if (e != null || !Thread.interrupted())
return e;
throw new InterruptedException();
}
/**
构造函数
TransferQueue代表公平处理方式
TransferStack代表不公平处理方式
*/
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
//这个是 TransferQueue 中的每一个节点
// 队列可全是消费者或全是生产者
static final class QNode {
volatile QNode next;
volatile Object item;
// 如果执行了put、take方法时,需要挂起线程,而挂起的就是这个线程
volatile Thread waiter;
// true:生产者
// false:消费者
final boolean isData;
}
/** Head of queue */
transient volatile QNode head;
/** Tail of queue */
transient volatile QNode tail;
// 无参构造
TransferQueue() {
// 会先初始化一个QNode,作为head和tail的指向,并且这个QNode不包含线程信息,就一个伪的头结点
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
源码分析
因为存数据、取数据都是调用 transfer() 方法,所以这里看下 TransferQueue.transfer 方法
E transfer(E e, boolean timed, long nanos) {
// 声明QNode
QNode s = null;
// 判断当前操作的是消费者还是生产者
// true:生产者
// false:消费者
boolean isData = (e != null);
for (;;) {
// 头尾节点
QNode t = tail;
QNode h = head;
if (t == null || h == null) // saw uninitialized value
continue; // spin
// 如果头和尾相等。
// 在当前的QNode单向链表中,要么都存放生产者,要么都存放消费者。
// 所以第二个判断是,如果队列中有Qnode,查看我当前的isData是否和队列中Qnode的isData一致,一致挂上去
if (h == t || t.isData == isData) { // empty or same-mode
QNode tn = t.next;
if (t != tail) // inconsistent read
continue;
// 如果尾节点的next不为null,有并发情况
if (tn != null) { // lagging tail
// 直接CAS操作,将tail的next节点设置为tail节点
advanceTail(t, tn);
continue;
}
// timed == true: offer,poll
// 进来之后没有立即配对,那就直接告辞!
if (timed && nanos <= 0) // can't wait
return null;
// 初始化节点
if (s == null)
s = new QNode(e, isData);
// 将tail的next指向的当前QNode
if (!t.casNext(null, s)) // failed to link in
continue;
// 将tail指向当前Qnode
advanceTail(t, s); // swing tail and wait
// 挂起线程直到被唤醒拿到指定数据
Object x = awaitFulfill(s, e, timed, nanos);
// 拿到的数据和当前QNode一致,当前节点取消了
if (x == s) { // wait was cancelled
// 清除当前节点
clean(t, s);
return null;
}
// 判断是否还在队列中
if (!s.isOffList()) { // not already unlinked
// 将当前节点设置为新的head
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
// 我拿到数据了,设置item为当前节点对象
s.item = s;
// 线程置位null
s.waiter = null;
}
// 返回数据
return (x != null) ? (E)x : e;
} else { // complementary-mode
// 需要配对的数据
QNode m = h.next; // node to fulfill
// 并发问题,重新循环
if (t != tail || m == null || h != head)
continue; // inconsistent read
// 拿到m中的数据。
// x == null:队列是消费者
// x != null:队列是生产者
Object x = m.item;
// 1、出现并发问题
// 2、取出的数据,竟然是节点本身,代表节点被取消!
// 3、开始交换数据,将当前方法传入数据,替换到head的next,如果操作失败,并发问题
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
// 操作成功,也要替换head
advanceHead(h, m); // successfully fulfilled
// 唤醒队列中的head的next节点的线程。
LockSupport.unpark(m.waiter);
// 操作成功,返回数据
return (x != null) ? (E)x : e;
}
}
}