线程安全的队列分为阻塞队列和非阻塞队列。
非阻塞队列ConcurrentLinkedQueue
使用 CAS 非阻塞算法来实现线程安全,无界非阻塞队列
属性
transient volatile Node<E> head;
private transient volatile Node<E> tail;
构造方法
public ConcurrentLinkedQueue() {
head = tail = new Node<E>();//哨兵
}
加入元素
public boolean offer(E e) {
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
//从尾部插入节点
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
// p是最后一个节点
if (q == null) {
//使用CAS插入节点
if (NEXT.compareAndSet(p, null, newNode)) {
if (p != t) // hop two nodes at a time; failure is OK
TAIL.weakCompareAndSet(this, t, newNode);
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
阻塞队列BlockingQueue
提供了可阻塞的插入和移除方法。当队列满时,执行插入操作的线程会被阻塞;当队里空时,执行移除方法的线程被阻塞。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中。
常用方法
抛出异常 | 返回值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove(e) | poll () | take() | poll(time,unit) |
检查 | element(e) | peek() | – | – |
抛出异常:如果操作无法立即执行,抛异常。
返回值:如果操作无法立即执行,返回一个特定的值( true / false)。
阻塞:如果操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。
LinkedBlockingQueue
可以通过构造函数设置是否有界,使用ReentrantLock,Condition和Atomic实现
数据结构
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
属性
读取分别有一把锁,可以同时进行
//容量
private final int capacity;
//元素数量
private final AtomicInteger count = new AtomicInteger();
//哨兵
transient Node<E> head;
/** 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();
构造方法
//无参,容量为Integer.MAX_VALUE
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
加入元素
add()方法内部调用offer(),offer返回boolean类型数据,add会抛出异常
add()是AbstractQueue提供的方法
LinkedBlockingQueue提供了put和offer方法
offer()在队列满时会直接返回false,在没有获取到锁时会一直等待
put()在队列满时会加入等待队列,没有获取到锁时可以被中断等待
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
//如果队列已满 返回false
if (count.get() == capacity)
return false;
final int c;
final Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
//加锁,待获取锁成功后,才响应中断。
putLock.lock();
try {
//如果队列已满 返回false
if (count.get() == capacity)
return false;
//入队
enqueue(node);
//count原子自增
c = count.getAndIncrement();
if (c + 1 < capacity)
//唤醒condition队列中阻塞put()的线程
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();//唤醒阻塞在take()的线程
return true;
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
final int c;
final Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
//加锁,允许在等待时由其他线程的Thread.interrupt()方法来中断等待
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();
}
ArrayBlockingQueue:有界队列
属性
读取共用同一把锁,不能同时进行。
也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。
final Object[] items;
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
//队列中元素个数
int count;//由于count是int类型,不能修改成两把锁。
构造方法
需要指定容量,是一个有界队列
//默认是非公平锁
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
//可以指定是否使用公平锁
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();
}
PriorityBlockingQueue
支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。
SynchronousQueue
每个插入操作必须等待另一个线程的对应移除操作,每移除操作必须等待另一个线程的对应插入操作,队列没有任何内部容量。