JDK7中SynchronousQueue源码解析
来源:互联网 发布:ubuntu和redhat的区别 编辑:程序博客网 时间:2024/05/17 23:34
SynchronousQueue<E>是java.util.concurrent包下的阻塞队列类,是Java Collections Framework的成员,其中每个插入操作必须等待另一个线程的对应移除操作,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。不能在同步队列上进行peek,因为仅在试图要移除元素时,该元素才存在;除非另一个线程试图移除某个元素,否则也不能使用任何方法插入元素;也不能迭代队列,因为其中没有元素可用于迭代。队列的头是尝试添加到队列中的首个已排队插入线程的元素;如果没有这样的已排队线程,则没有可用于移除的元素并且poll()将会返回null。对于其它Collection方法,例如contains,SynchronousQueue作为一个空collection。队列不允许null元素。
SynchronousQueue<E>非常适合于传递性设计,在线程中运行的对象要将某些信息,事件或任务传递给在另外线程中运行的对象,它就必须与该对象同步。对于正在等待的生产者和使用者线程而言,此类支持可选的公平排序策略。默认情况下不保证这种排序。但是使用公平设置为true所构造的队列可保证线程以FIFO的顺序进行访问。此类及其迭代器实现Collection和Iterator接口的所有可选方法。
运用实现可参考:https://github.com/chunericli/wise-utils,相对于ArrayBlockingQueue和LinkedBlockingQueue而言,SynchronousQueue<E>的运用和实现相对比较复杂。SynchronousQueue<E>提供的实现方法虽然在行为上有不少差异,但是其底层实现极其相似,SynchronousQueue<E>提供的方法不多,其API提供【插入,获取和移除】和差别概述如下所示:
构造函数:
SynchronousQueue():创建一个具有非公平访问策略的SynchronousQueue。
SynchronousQueue(boolean fair):创建一个具有指定公平策略的SynchronousQueue。
移除元素:
int drainTo(Collection<? super E> c):移除队列中所有可用元素,并将其添加到给定collection中。
int drainTo(Collection<? super E> c, int maxElements):最多从队列中移除给定数量的可用元素,并将这些元素添加到给定collection中。
boolean remove(Object o):始终返回false。
boolean removeAll(Collection<?> c):始终返回false。
void clear():不执行任何操作。
插入元素:
boolean offer(E e):如果另一个线程正在等待以便接收指定元素,则将指定元素插入到此队列。
boolean offer(E o, long timeout, TimeUnit unit):将指定元素插入到此队列,如有必要则等待指定的时间,以便另一个线程接收它。
void put(E o):将指定元素添加到此队列,如有必要则等待另一个线程接收它。
获取元素:
E peek():始终返回null。
E poll():如果另一个线程当前正要使用某个元素,则获取并移除此队列的头。
E poll(long timeout, TimeUnit unit):获取并移除此队列的头,如有必要则等待指定的时间,以便另一个线程插入它。
E take():获取并移除此队列的头,如有必要则等待另一个线程插入它。
SynchronousQueue<E>实现了Scherer和Scott的"非阻塞并发对象与条件同步"中描述的双堆栈和双队列算法的扩展。关于非阻塞并发对象与条件同步算法可自行参考:http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html。栈(LIFO)用于非公平模式,队列(FIFO)用于公平模式,两者的性能通常是相似的。FIFO通常支持在争用下提高吞吐量,但是LIFO在通用应用程序中维护更高的线程位置。
双队列(以及类似的堆栈)是在任何给定时间内保存"数据"【通过put操作提供的items或者"请求"代表操作的slots】或者是空的。调用"完成"(即请求从队列中保存数据或反之亦然的请求)出队列的互补节点。这些队列最有趣的特性是,任何操作都可以计算出队列所在的模式,并在不需要锁的情况下采取相应的操作。队列和堆栈都扩展抽象类Transferer,定义执行put或take的单个方法transfer。由于在双数据结构中,put和take操作是对称的,所以几乎所有的代码都可以组合在一起。由此产生的transfer方法在较长一段时间内,但如果分解成几乎重复的部分,则比它们更容易遵循。队列和堆栈数据结构具有许多概念上的相似性,但具体细节很少。为了简单起见,它们是保持不同的,这样它们就可以单独演化。这里的算法不同于上面的内容中的版本,用于扩展它们以在同步队列中使用,以及处理取消。主要的差异包括:
(1)最初的算法使用了位标记指针,但是这里的这些算法在节点中使用模式位,从而导致了一些进一步的适应。
(2)同步队列必须阻塞等待实现的线程。
(3)通过超时和中断支持取消,包括清理已取消的节点/线程,以避免垃圾保留和内存耗尽。
SynchronousQueue<E>主要是使用LockSupport.park/unpark来完成阻塞相关操作的,除了那些看起来是接下来要实现的节点(spin a bit,仅在多处理器上)。在非常繁忙的同步队列中,自旋可以显著提高吞吐量。而在不太忙碌的情况下,自旋量很小,不值得注意。在队列vs堆栈中,以不同的方式进行清理。对于队列,当它被取消时,几乎可以在O(1)时间内立即删除一个节点(模块重试的一致性检查)。但如果它可以被固定为当前的尾节点,它必须等到随后的一些取消。对于堆栈,需要可能O(n)遍历以确保可以删除节点,但这可以与访问堆栈的其它线程同时运行。
虽然垃圾回收处理了大多数节点回收问题,但这些问题使得非阻塞算法变得复杂,但是关注的是"forget"对数据,其它节点和可能被阻塞的线程长期持有的线程的引用。如果在设置为null的情况下与主算法发生冲突,则通过更改节点的链接到节点本身来完成。对于堆栈节点(因为阻塞的线程不挂在老的head指针上),这并没有多大的影响,但是队列节点中的引用必须被积极地遗忘,以避免到达后所提到的任何节点的可达性。
SynchronousQueue<E>源码解析:
public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
abstract static class Transferer{
// 双堆栈和双队列的内部API,用来执行put和take操作
abstract Object transfer(Object e, boolean timed, long nanos);
}
static final int maxUntimedSpins = maxTimedSpins * 16; // 不用在自旋时检查是否超时
static final long spinForTimeoutThreshold = 1000L;
static final class TransferStack extends Transferer{
static final int REQUEST = 0; // 未实现的消费者
static final int DATA = 1; // 未实现的生产者
static final int FULFILLING = 2; // 实现其它未实现的DATA or REQUEST
static boolean isFulfilling(int m) { return (m&FULFILLING)!=0; }
static final class SNode{
volatile SNode next; // next node in stack
volatile SNode match; // the node matched to this
volatile Thread waiter; // to control park/unpark
Object item; // data; or null for REQUESTs
int mode; // item和mode不需要是volatile或者volatile/atomic操作,因为总是写在前读在后
volatile SNode head; // 栈的头节点
SNode(Object item) { this.item=item; }
// 将节点推到栈是懒加载创建以及在可能的情况下被重用,可帮助减少读取和CASes之间的间隔,并避免在发生争用时导致节点失败的情况。
s.mode = mode;
s.next = next;
return s;
}
boolean casHead(SNode h, SNode nh){ // (this==h)=>(this==nh)
return h==head&&UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
boolean casNext(SNode cmp, SNode val){
return cmp==next&&UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
void tryCancel(){ // 试图取消对其本身的匹配节点的等待
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
boolean isCancelled(){ return match == this; }
// 试图将节点s匹配到该节点,如果是则唤醒线程。Fulfillers调用tryMatch来识别它们的waiters,waiters将被阻塞直到被匹配为止。
boolean tryMatch(SNode s){
if(match==null&&UNSAFE.compareAndSwapObject(this, matchOffset, null, s)){
Thread w=waiter;
if(w!=null){ // waiters need at most one unpark
waiter=null;
LockSupport.unpark(w);
}
return true;
}
return match==s;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static{ // 通过UNSAFE实现内存偏移量的初始化
try{
UNSAFE=sun.misc.Unsafe.getUnsafe();
Class k=SNode.class;
matchOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("match"));
nextOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
}catch(Exception e){
throw new Error(e);
}
}
// SNode是TransferStack的内部类,此处节点作用类似AQS的CLH队列,辅助实现类【个人理解】
}
// 如果显然是空的或者已经包含了相同模式的节点,那么尝试在堆栈上推送节点,等待匹配,返回该节点,如果取消则返回null;如果明显包含互补模式的节点,尝试将一个实现节点推送到堆栈上,与对应的等待节点匹配,从堆栈中弹出,并返回匹配项。由于其它线程执行动作3,因此可能不需要匹配或解除链接;如果堆栈顶部已经拥有另一个满足的节点,通过执行它的匹配和/或pop操作来帮助它,然后继续。帮助的代码基本上与实现相同,只是它没有返回项目。
int mode=(e==null)?REQUEST:DATA; // 未实现的消费者或者是生产者
for(;;){ // 循环调用,其实是自旋【个人理解】
SNode h = head;
if(h==null||h.mode==mode){ // 空或者是相同模式
if(timed&&nanos<=0){ // 不等待
if (h!=null&&h.isCancelled()) // 尝试在堆栈上推送节点,等待匹配,返回该节点
casHead(h,h.next); // 弹出取消的节点
else
return null; // 如果取消则返回null
}else if(casHead(h, s=snode(s,e,h,mode))){
SNode m=awaitFulfill(s,timed,nanos);
if(m==s){ // wait was cancelled
clean(s);
return null;
}
if((h=head)!=null&&h.next==s)
casHead(h, s.next); // help s's fulfiller
return (mode == REQUEST) ? m.item : s.item;
}
}else if(!isFulfilling(h.mode)){ // try to fulfill
if(h.isCancelled()) // already cancelled
casHead(h,h.next); // pop and retry
else if(casHead(h,s=snode(s,e,h,FULFILLING|mode))){
for(;;){ // loop until matched or waiters disappear
SNode m=s.next; // m is s's match
if(m==null){ // all waiters are gone
casHead(s,null); // pop fulfill node
s=null; // use new node next time
break; // restart main loop
}
SNode mn=m.next;
if(m.tryMatch(s)){
casHead(s,mn); // pop both s and m
return(mode==REQUEST)?m.item:s.item;
} else // lost match
s.casNext(m,mn); // help unlink
}
}
}else{ // 栈顶部已经存在实现的节点
SNode m=h.next; // m is h's match
if(m=null) // waiter is gone
casHead(h,null); // pop fulfilling node
else{
SNode mn=m.next;
if(m.tryMatch(h)) // help match
casHead(h,mn); // pop both h and m
else // lost match
h.casNext(m,mn); // help unlink
}
}
}
}
// 当节点/线程要阻塞时,首先设置waiter字段,然后在实际阻塞前至少再重新检查一次状态,因此覆盖race vs fulfiller注意到waiter是非空的,所以应该被唤醒。当被节点在堆栈的顶端调用时,调用park之前是自旋,以避免在生产者和消费者及时到达时阻塞。这种情况只会发生在多处理器上。从主循环中返回的检查顺序反映了一个事实,即中断优先于正常的返回,这优先于超时(因此在超时时,在放弃前最后检查匹配)。除了来自不定时同步队列的调用。{poll/offer}不要检查中断,不要等待,所以被困在transfer方法中,而不是调用awaitFulfill。
SNode awaitFulfill(SNode s,boolean timed,long nanos){ // 自旋/阻塞直到节点被完成操作匹配到
long lastTime=timed?System.nanoTime():0;
Thread w=Thread.currentThread();
SNode h=head;
int spins=(shouldSpin(s)?(timed?maxTimedSpins:maxUntimedSpins):0);
for(;;){
if(w.isInterrupted())
s.tryCancel();
SNode m=s.match;
if(m!=null)
return m;
if(timed){
long now=System.nanoTime();
nanos-=now-lastTime;
lastTime=now;
if(nanos<=0){
s.tryCancel();
continue;
}
}
if(spins>0)
spins=shouldSpin(s)?(spins-1):0;
else if(s.waiter==null)
s.waiter=w; // establish waiter so can park next iter
else if(!timed)
LockSupport.park(this);
else if(nanos>spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
boolean shouldSpin(SNode s) { // 头节点或者存在活跃的fulfiller则返回成功
SNode h = head;
return (h == s || h == null || isFulfilling(h.mode));
}
// 在最坏的情况下,可能需要遍历整个堆栈来unlink s。如果存在多个并发调用来清理,可能不会看到其它线程已经删除了它。但是可以在看到任何已知的节点时停止调用s。使用s.next除非它也被取消,在这种情况下会尝试一个过去的节点。没有进一步检查,因为不想为了找到sentinel而加倍地遍历。
void clean(SNode s){ // Unlinks s from the stack
s.item = null; // forget item
s.waiter = null; // forget thread
SNode past = s.next;
if(past != null && past.isCancelled())
past = past.next;
SNode p; // Absorb cancelled nodes at head
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);
while (p != null && p != past) { // Unsplice embedded nodes
SNode n = p.next;
if(n != null && n.isCancelled())
p.casNext(n, n.next);
else
p = n;
}
}
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
static{
try{
UNSAFE=sun.misc.Unsafe.getUnsafe();
Class k=TransferStack.class;
headOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("head"));
}catch(Exception e){
throw new Error(e);
}
}
// TransferStack的实现比较复杂,但是底层不复杂【是CAS+Node节点来实现】,由于需要考虑情形多,故实现并不容易。
}
static final class TransferQueue extends Transferer {
static final class QNode {
volatile QNode next; // next node in queue
volatile Object item; // CAS'ed to or from null
volatile Thread waiter; // to control park/unpark
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
boolean casNext(QNode cmp, QNode val) {
return next == cmp && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp && UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void tryCancel(Object cmp) { // 试图以CAS引用来取消此item
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
boolean isCancelled() { return item == this; }
boolean isOffList() { // 如果节点已被队列取消则返回true,因为它的下一个指针由于advanceHead操作而被遗忘。
return next == this;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static{
try{
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = QNode.class;
itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
}catch(Exception e){
throw new Error(e);
}
}
}
transient volatile QNode head; // Head of queue
transient volatile QNode tail; // Tail of queue
transient volatile QNode cleanMe; // 引用已被取消的节点,该节点可能还没有从队列中删除,因为它是在它被取消时最后插入的节点。
TransferQueue() {
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
h.next = h; // forget old next
}
void advanceTail(QNode t, QNode nt) { // Tries to cas nt as new tail
if(tail == t) UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
}
boolean casCleanMe(QNode cmp, QNode val) { // Tries to CAS cleanMe slot
return cleanMe == cmp && UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
}
// 如果队列显然包含等待项,而这个调用是互补模式,则尝试在等待节点的CAS item字段中执行,并将其排出,然后返回匹配项。在每种情况下,检查并试着代表其它被搁置/慢的线程帮助advance head and tail。循环从null检查开始,防止看到未初始化的头部或尾部值。这在当前的同步队列中是不会发生的,但是如果调用者持有non-volatile/final ref则可能。无论如何,检查都在这里,因为它将空检查放在循环的顶部,这通常比让它们隐式插入要快。
Object transfer(Object e, boolean timed, long nanos) { // Puts or takes an item
QNode s = null; // constructed/reused as needed
boolean isData = (e != null);
for(;;){
QNode t = tail;
QNode h = head;
if(t==null||h==null) // 未被初始化的值
continue; // spin
if(h == t||t.isData==isData) { // 空或持有相同模式的节点
QNode tn = t.next;
if(t!=tail) continue; // 读不一致
if(tn != null){ // lagging tail
advanceTail(t, tn);
continue;
}
if(timed&&nanos<=0) return null; // can't wait
if(s==null) s = new QNode(e,isData);
if(!t.casNext(null, s)) // failed to link in
continue;
advanceTail(t, s); // swing tail and wait
Object x = awaitFulfill(s, e, timed, nanos);
if(x == s){ // wait was cancelled
clean(t, s);
return null;
}
if(!s.isOffList()) { // not already unlinked
advanceHead(t, s); // unlink if head
if(x!=null) // and forget fields
s.item = s;
s.waiter = null;
} // 尝试将节点添加到等待服务的队列中,等待完成(或取消)并返回匹配项
return (x != null) ? x : e;
}else{ // complementary-mode
QNode m = h.next; // node to fulfill
if(t!=tail||m == null||h!= head)
continue; // inconsistent read
Object x = m.item;
if(isData==(x != null)|| // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
advanceHead(h, m); // successfully fulfilled
LockSupport.unpark(m.waiter);
return (x != null) ? x : e;
}
}
}
Thread w = Thread.currentThread();
int spins = ((head.next == s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0);
for(;;){
if (w.isInterrupted()) s.tryCancel(e);
Object x = s.item;
if (x != e) return x;
if(timed){
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
if(nanos <= 0){
s.tryCancel(e); continue;
}
}
if(spins > 0) --spins;
else if (s.waiter == null) s.waiter = w;
else if (!timed) LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
// 在任何给定的时间,不能删除list上的最后插入的节点。为适应这点,如果不能删除s则将其前继节点保存为"cleanMe",删除先前保存的版本。至少存在节
点s或之前保存的节点可以被删除,所以这个总是终止。
void clean(QNode pred, QNode s) { // 摆脱前继节点s和原始前继节点pred
s.waiter = null; // forget thread
while (pred.next == s) { // Return early if already unlinked
QNode h = head;
QNode hn = h.next; // Absorb cancelled first node as head
if(hn != null && hn.isCancelled()){
advanceHead(h, hn); continue;
}
QNode t = tail; // Ensure consistent read for tail
if(t == h) return;
QNode tn = t.next;
if (t != tail) continue;
if(tn != null) {
advanceTail(t, tn);
continue;
}
if(s != t){ // If not tail, try to unsplice
QNode sn = s.next;
if(sn == s || pred.casNext(s, sn)) return;
}
QNode dp = cleanMe;
if(dp != null){ // Try unlinking previous cancelled node
QNode d = dp.next;
QNode dn;
if(d == null || // d is gone or
d == dp || // d is off list or
!d.isCancelled() || // d not cancelled or
(d!=t && // d not tail and
(dn = d.next) != null && // has successor
dn != d && // that is on list
dp.casNext(d, dn))) // d unspliced
casCleanMe(dp, null);
if(dp == pred) return; // s is already saved node
}else if(casCleanMe(null,pred))
return; // Postpone cleaning s
}
}
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
private static final long tailOffset;
private static final long cleanMeOffset;
static{
try{
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = TransferQueue.class;
headOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("head"));
tailOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("tail"));
cleanMeOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("cleanMe"));
}catch(Exception e){
throw new Error(e);
}
}
// TransferQueue的实现也比较复杂,但是底层同样不复杂【是CAS+Node节点来实现】。
}
// 只在构造函数中设置但不能声明为final而让序列化不会更加复杂。由于最多只在公共方法中访问一次,因此在使用volatile而不是final时没有明显的性能
损失。
private transient volatile Transferer transferer;
public SynchronousQueue(boolean fair) { // 默认为false
transferer = fair ? new TransferQueue() : new TransferStack();
}
if (transferer.transfer(o, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
// 插入元素到队列,为其它线程接收元素,等待如有必要,具有等待时限
public boolean offer(E o, long timeout, TimeUnit unit)throws InterruptedException {
if(o == null) throw new NullPointerException();
if(transferer.transfer(o, true, unit.toNanos(timeout)) != null)
return true;
if(!Thread.interrupted())
return false;
throw new InterruptedException();
}
public boolean offer(E e) { // 插入元素到队列,为其它线程接收元素,等待如有必要
if(e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
if (e != null) return (E)e;
Thread.interrupted();
throw new InterruptedException();
}
// 检索和移除队列头节点,为其它线程插入,等待如有必要,具有等待时限
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
Object e = transferer.transfer(null, true, unit.toNanos(timeout));
if(e != null || !Thread.interrupted())
return (E)e;
throw new InterruptedException();
}
public int drainTo(Collection<? super E> c, int maxElements) { // 将队列中的元素复制到集合中
if(c == null) throw new NullPointerException();
if(c == this) throw new IllegalArgumentException();
int n = 0;
E e;
// 同drainTo(Collection<? super E> c)的实现差异在于:n < maxElements的判断
while(n < maxElements && (e = poll()) != null) {
c.add(e);
++n;
}
return n;
}
// SynchronousQueue提供的方法的实现是Transferer.transferer()方法的灵活运用
}
实现总结:SynchronousQueue内部维护了Transferer对象,可根据SynchronousQueue(boolean fair)构造函数对公平性的设置,Transferer可初始化为TransferQueue【true】,TransferStack【默认false】,SynchronousQueue所提供的所有方法的实现【插入和获取元素】几乎全部交由Transferer的transferer(Object e, boolean timed, long nanos)方法实现。Transferer的底层由LockSupport,CAS和内置的Node来实现的,通过CAS Head节点来实现获取或者插入元素。
SynchronousQueue<E>非常适合于传递性设计,在线程中运行的对象要将某些信息,事件或任务传递给在另外线程中运行的对象,它就必须与该对象同步。对于正在等待的生产者和使用者线程而言,此类支持可选的公平排序策略。默认情况下不保证这种排序。但是使用公平设置为true所构造的队列可保证线程以FIFO的顺序进行访问。此类及其迭代器实现Collection和Iterator接口的所有可选方法。
运用实现可参考:https://github.com/chunericli/wise-utils,相对于ArrayBlockingQueue和LinkedBlockingQueue而言,SynchronousQueue<E>的运用和实现相对比较复杂。SynchronousQueue<E>提供的实现方法虽然在行为上有不少差异,但是其底层实现极其相似,SynchronousQueue<E>提供的方法不多,其API提供【插入,获取和移除】和差别概述如下所示:
构造函数:
SynchronousQueue():创建一个具有非公平访问策略的SynchronousQueue。
SynchronousQueue(boolean fair):创建一个具有指定公平策略的SynchronousQueue。
移除元素:
int drainTo(Collection<? super E> c):移除队列中所有可用元素,并将其添加到给定collection中。
int drainTo(Collection<? super E> c, int maxElements):最多从队列中移除给定数量的可用元素,并将这些元素添加到给定collection中。
boolean remove(Object o):始终返回false。
boolean removeAll(Collection<?> c):始终返回false。
void clear():不执行任何操作。
插入元素:
boolean offer(E e):如果另一个线程正在等待以便接收指定元素,则将指定元素插入到此队列。
boolean offer(E o, long timeout, TimeUnit unit):将指定元素插入到此队列,如有必要则等待指定的时间,以便另一个线程接收它。
void put(E o):将指定元素添加到此队列,如有必要则等待另一个线程接收它。
获取元素:
E peek():始终返回null。
E poll():如果另一个线程当前正要使用某个元素,则获取并移除此队列的头。
E poll(long timeout, TimeUnit unit):获取并移除此队列的头,如有必要则等待指定的时间,以便另一个线程插入它。
E take():获取并移除此队列的头,如有必要则等待另一个线程插入它。
SynchronousQueue<E>实现了Scherer和Scott的"非阻塞并发对象与条件同步"中描述的双堆栈和双队列算法的扩展。关于非阻塞并发对象与条件同步算法可自行参考:http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html。栈(LIFO)用于非公平模式,队列(FIFO)用于公平模式,两者的性能通常是相似的。FIFO通常支持在争用下提高吞吐量,但是LIFO在通用应用程序中维护更高的线程位置。
双队列(以及类似的堆栈)是在任何给定时间内保存"数据"【通过put操作提供的items或者"请求"代表操作的slots】或者是空的。调用"完成"(即请求从队列中保存数据或反之亦然的请求)出队列的互补节点。这些队列最有趣的特性是,任何操作都可以计算出队列所在的模式,并在不需要锁的情况下采取相应的操作。队列和堆栈都扩展抽象类Transferer,定义执行put或take的单个方法transfer。由于在双数据结构中,put和take操作是对称的,所以几乎所有的代码都可以组合在一起。由此产生的transfer方法在较长一段时间内,但如果分解成几乎重复的部分,则比它们更容易遵循。队列和堆栈数据结构具有许多概念上的相似性,但具体细节很少。为了简单起见,它们是保持不同的,这样它们就可以单独演化。这里的算法不同于上面的内容中的版本,用于扩展它们以在同步队列中使用,以及处理取消。主要的差异包括:
(1)最初的算法使用了位标记指针,但是这里的这些算法在节点中使用模式位,从而导致了一些进一步的适应。
(2)同步队列必须阻塞等待实现的线程。
(3)通过超时和中断支持取消,包括清理已取消的节点/线程,以避免垃圾保留和内存耗尽。
SynchronousQueue<E>主要是使用LockSupport.park/unpark来完成阻塞相关操作的,除了那些看起来是接下来要实现的节点(spin a bit,仅在多处理器上)。在非常繁忙的同步队列中,自旋可以显著提高吞吐量。而在不太忙碌的情况下,自旋量很小,不值得注意。在队列vs堆栈中,以不同的方式进行清理。对于队列,当它被取消时,几乎可以在O(1)时间内立即删除一个节点(模块重试的一致性检查)。但如果它可以被固定为当前的尾节点,它必须等到随后的一些取消。对于堆栈,需要可能O(n)遍历以确保可以删除节点,但这可以与访问堆栈的其它线程同时运行。
虽然垃圾回收处理了大多数节点回收问题,但这些问题使得非阻塞算法变得复杂,但是关注的是"forget"对数据,其它节点和可能被阻塞的线程长期持有的线程的引用。如果在设置为null的情况下与主算法发生冲突,则通过更改节点的链接到节点本身来完成。对于堆栈节点(因为阻塞的线程不挂在老的head指针上),这并没有多大的影响,但是队列节点中的引用必须被积极地遗忘,以避免到达后所提到的任何节点的可达性。
SynchronousQueue<E>源码解析:
public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
abstract static class Transferer{
// 双堆栈和双队列的内部API,用来执行put和take操作
abstract Object transfer(Object e, boolean timed, long nanos);
}
static final int NCPUS = Runtime.getRuntime().availableProcessors();
// 应该是用于自旋控制的cpu数量
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32; // 阻塞前的自旋次数控制static final int maxUntimedSpins = maxTimedSpins * 16; // 不用在自旋时检查是否超时
static final long spinForTimeoutThreshold = 1000L;
static final class TransferStack extends Transferer{
static final int REQUEST = 0; // 未实现的消费者
static final int DATA = 1; // 未实现的生产者
static final int FULFILLING = 2; // 实现其它未实现的DATA or REQUEST
static boolean isFulfilling(int m) { return (m&FULFILLING)!=0; }
static final class SNode{
volatile SNode next; // next node in stack
volatile SNode match; // the node matched to this
volatile Thread waiter; // to control park/unpark
Object item; // data; or null for REQUESTs
int mode; // item和mode不需要是volatile或者volatile/atomic操作,因为总是写在前读在后
volatile SNode head; // 栈的头节点
SNode(Object item) { this.item=item; }
// 将节点推到栈是懒加载创建以及在可能的情况下被重用,可帮助减少读取和CASes之间的间隔,并避免在发生争用时导致节点失败的情况。
static SNode snode(SNode s, Object e, SNode next, int mode){
// 创建或重置节点字段,只从transfer中被调用
if(s==null) s=new SNode(e);s.mode = mode;
s.next = next;
return s;
}
boolean casHead(SNode h, SNode nh){ // (this==h)=>(this==nh)
return h==head&&UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
boolean casNext(SNode cmp, SNode val){
return cmp==next&&UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
void tryCancel(){ // 试图取消对其本身的匹配节点的等待
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
boolean isCancelled(){ return match == this; }
// 试图将节点s匹配到该节点,如果是则唤醒线程。Fulfillers调用tryMatch来识别它们的waiters,waiters将被阻塞直到被匹配为止。
boolean tryMatch(SNode s){
if(match==null&&UNSAFE.compareAndSwapObject(this, matchOffset, null, s)){
Thread w=waiter;
if(w!=null){ // waiters need at most one unpark
waiter=null;
LockSupport.unpark(w);
}
return true;
}
return match==s;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static{ // 通过UNSAFE实现内存偏移量的初始化
try{
UNSAFE=sun.misc.Unsafe.getUnsafe();
Class k=SNode.class;
matchOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("match"));
nextOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
}catch(Exception e){
throw new Error(e);
}
}
// SNode是TransferStack的内部类,此处节点作用类似AQS的CLH队列,辅助实现类【个人理解】
}
// 如果显然是空的或者已经包含了相同模式的节点,那么尝试在堆栈上推送节点,等待匹配,返回该节点,如果取消则返回null;如果明显包含互补模式的节点,尝试将一个实现节点推送到堆栈上,与对应的等待节点匹配,从堆栈中弹出,并返回匹配项。由于其它线程执行动作3,因此可能不需要匹配或解除链接;如果堆栈顶部已经拥有另一个满足的节点,通过执行它的匹配和/或pop操作来帮助它,然后继续。帮助的代码基本上与实现相同,只是它没有返回项目。
Object transfer(Object e, boolean timed, long nanos){
// TransferStack核心方法,用来实现插入或获取元素
SNode s=null; // 根据需要构造或者重用int mode=(e==null)?REQUEST:DATA; // 未实现的消费者或者是生产者
for(;;){ // 循环调用,其实是自旋【个人理解】
SNode h = head;
if(h==null||h.mode==mode){ // 空或者是相同模式
if(timed&&nanos<=0){ // 不等待
if (h!=null&&h.isCancelled()) // 尝试在堆栈上推送节点,等待匹配,返回该节点
casHead(h,h.next); // 弹出取消的节点
else
return null; // 如果取消则返回null
}else if(casHead(h, s=snode(s,e,h,mode))){
SNode m=awaitFulfill(s,timed,nanos);
if(m==s){ // wait was cancelled
clean(s);
return null;
}
if((h=head)!=null&&h.next==s)
casHead(h, s.next); // help s's fulfiller
return (mode == REQUEST) ? m.item : s.item;
}
}else if(!isFulfilling(h.mode)){ // try to fulfill
if(h.isCancelled()) // already cancelled
casHead(h,h.next); // pop and retry
else if(casHead(h,s=snode(s,e,h,FULFILLING|mode))){
for(;;){ // loop until matched or waiters disappear
SNode m=s.next; // m is s's match
if(m==null){ // all waiters are gone
casHead(s,null); // pop fulfill node
s=null; // use new node next time
break; // restart main loop
}
SNode mn=m.next;
if(m.tryMatch(s)){
casHead(s,mn); // pop both s and m
return(mode==REQUEST)?m.item:s.item;
} else // lost match
s.casNext(m,mn); // help unlink
}
}
}else{ // 栈顶部已经存在实现的节点
SNode m=h.next; // m is h's match
if(m=null) // waiter is gone
casHead(h,null); // pop fulfilling node
else{
SNode mn=m.next;
if(m.tryMatch(h)) // help match
casHead(h,mn); // pop both h and m
else // lost match
h.casNext(m,mn); // help unlink
}
}
}
}
// 当节点/线程要阻塞时,首先设置waiter字段,然后在实际阻塞前至少再重新检查一次状态,因此覆盖race vs fulfiller注意到waiter是非空的,所以应该被唤醒。当被节点在堆栈的顶端调用时,调用park之前是自旋,以避免在生产者和消费者及时到达时阻塞。这种情况只会发生在多处理器上。从主循环中返回的检查顺序反映了一个事实,即中断优先于正常的返回,这优先于超时(因此在超时时,在放弃前最后检查匹配)。除了来自不定时同步队列的调用。{poll/offer}不要检查中断,不要等待,所以被困在transfer方法中,而不是调用awaitFulfill。
SNode awaitFulfill(SNode s,boolean timed,long nanos){ // 自旋/阻塞直到节点被完成操作匹配到
long lastTime=timed?System.nanoTime():0;
Thread w=Thread.currentThread();
SNode h=head;
int spins=(shouldSpin(s)?(timed?maxTimedSpins:maxUntimedSpins):0);
for(;;){
if(w.isInterrupted())
s.tryCancel();
SNode m=s.match;
if(m!=null)
return m;
if(timed){
long now=System.nanoTime();
nanos-=now-lastTime;
lastTime=now;
if(nanos<=0){
s.tryCancel();
continue;
}
}
if(spins>0)
spins=shouldSpin(s)?(spins-1):0;
else if(s.waiter==null)
s.waiter=w; // establish waiter so can park next iter
else if(!timed)
LockSupport.park(this);
else if(nanos>spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
boolean shouldSpin(SNode s) { // 头节点或者存在活跃的fulfiller则返回成功
SNode h = head;
return (h == s || h == null || isFulfilling(h.mode));
}
// 在最坏的情况下,可能需要遍历整个堆栈来unlink s。如果存在多个并发调用来清理,可能不会看到其它线程已经删除了它。但是可以在看到任何已知的节点时停止调用s。使用s.next除非它也被取消,在这种情况下会尝试一个过去的节点。没有进一步检查,因为不想为了找到sentinel而加倍地遍历。
void clean(SNode s){ // Unlinks s from the stack
s.item = null; // forget item
s.waiter = null; // forget thread
SNode past = s.next;
if(past != null && past.isCancelled())
past = past.next;
SNode p; // Absorb cancelled nodes at head
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);
while (p != null && p != past) { // Unsplice embedded nodes
SNode n = p.next;
if(n != null && n.isCancelled())
p.casNext(n, n.next);
else
p = n;
}
}
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
static{
try{
UNSAFE=sun.misc.Unsafe.getUnsafe();
Class k=TransferStack.class;
headOffset=UNSAFE.objectFieldOffset(k.getDeclaredField("head"));
}catch(Exception e){
throw new Error(e);
}
}
// TransferStack的实现比较复杂,但是底层不复杂【是CAS+Node节点来实现】,由于需要考虑情形多,故实现并不容易。
}
static final class TransferQueue extends Transferer {
static final class QNode {
volatile QNode next; // next node in queue
volatile Object item; // CAS'ed to or from null
volatile Thread waiter; // to control park/unpark
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
boolean casNext(QNode cmp, QNode val) {
return next == cmp && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp && UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void tryCancel(Object cmp) { // 试图以CAS引用来取消此item
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
boolean isCancelled() { return item == this; }
boolean isOffList() { // 如果节点已被队列取消则返回true,因为它的下一个指针由于advanceHead操作而被遗忘。
return next == this;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static{
try{
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = QNode.class;
itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
}catch(Exception e){
throw new Error(e);
}
}
}
transient volatile QNode head; // Head of queue
transient volatile QNode tail; // Tail of queue
transient volatile QNode cleanMe; // 引用已被取消的节点,该节点可能还没有从队列中删除,因为它是在它被取消时最后插入的节点。
TransferQueue() {
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
void advanceHead(QNode h, QNode nh) {
// 试图cas nh作为新的head节点;如果成功则释放旧的head节点的next节点以避免GC保留
if(h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh))h.next = h; // forget old next
}
void advanceTail(QNode t, QNode nt) { // Tries to cas nt as new tail
if(tail == t) UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
}
boolean casCleanMe(QNode cmp, QNode val) { // Tries to CAS cleanMe slot
return cleanMe == cmp && UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
}
// 如果队列显然包含等待项,而这个调用是互补模式,则尝试在等待节点的CAS item字段中执行,并将其排出,然后返回匹配项。在每种情况下,检查并试着代表其它被搁置/慢的线程帮助advance head and tail。循环从null检查开始,防止看到未初始化的头部或尾部值。这在当前的同步队列中是不会发生的,但是如果调用者持有non-volatile/final ref则可能。无论如何,检查都在这里,因为它将空检查放在循环的顶部,这通常比让它们隐式插入要快。
Object transfer(Object e, boolean timed, long nanos) { // Puts or takes an item
QNode s = null; // constructed/reused as needed
boolean isData = (e != null);
for(;;){
QNode t = tail;
QNode h = head;
if(t==null||h==null) // 未被初始化的值
continue; // spin
if(h == t||t.isData==isData) { // 空或持有相同模式的节点
QNode tn = t.next;
if(t!=tail) continue; // 读不一致
if(tn != null){ // lagging tail
advanceTail(t, tn);
continue;
}
if(timed&&nanos<=0) return null; // can't wait
if(s==null) s = new QNode(e,isData);
if(!t.casNext(null, s)) // failed to link in
continue;
advanceTail(t, s); // swing tail and wait
Object x = awaitFulfill(s, e, timed, nanos);
if(x == s){ // wait was cancelled
clean(t, s);
return null;
}
if(!s.isOffList()) { // not already unlinked
advanceHead(t, s); // unlink if head
if(x!=null) // and forget fields
s.item = s;
s.waiter = null;
} // 尝试将节点添加到等待服务的队列中,等待完成(或取消)并返回匹配项
return (x != null) ? x : e;
}else{ // complementary-mode
QNode m = h.next; // node to fulfill
if(t!=tail||m == null||h!= head)
continue; // inconsistent read
Object x = m.item;
if(isData==(x != null)|| // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
advanceHead(h, m); // successfully fulfilled
LockSupport.unpark(m.waiter);
return (x != null) ? x : e;
}
}
}
Object awaitFulfill(QNode s, Object e, boolean timed, long nanos) {
// 自旋/阻塞直到节点fulfilled
long lastTime = timed ? System.nanoTime() : 0;Thread w = Thread.currentThread();
int spins = ((head.next == s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0);
for(;;){
if (w.isInterrupted()) s.tryCancel(e);
Object x = s.item;
if (x != e) return x;
if(timed){
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
if(nanos <= 0){
s.tryCancel(e); continue;
}
}
if(spins > 0) --spins;
else if (s.waiter == null) s.waiter = w;
else if (!timed) LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
// 在任何给定的时间,不能删除list上的最后插入的节点。为适应这点,如果不能删除s则将其前继节点保存为"cleanMe",删除先前保存的版本。至少存在节
点s或之前保存的节点可以被删除,所以这个总是终止。
void clean(QNode pred, QNode s) { // 摆脱前继节点s和原始前继节点pred
s.waiter = null; // forget thread
while (pred.next == s) { // Return early if already unlinked
QNode h = head;
QNode hn = h.next; // Absorb cancelled first node as head
if(hn != null && hn.isCancelled()){
advanceHead(h, hn); continue;
}
QNode t = tail; // Ensure consistent read for tail
if(t == h) return;
QNode tn = t.next;
if (t != tail) continue;
if(tn != null) {
advanceTail(t, tn);
continue;
}
if(s != t){ // If not tail, try to unsplice
QNode sn = s.next;
if(sn == s || pred.casNext(s, sn)) return;
}
QNode dp = cleanMe;
if(dp != null){ // Try unlinking previous cancelled node
QNode d = dp.next;
QNode dn;
if(d == null || // d is gone or
d == dp || // d is off list or
!d.isCancelled() || // d not cancelled or
(d!=t && // d not tail and
(dn = d.next) != null && // has successor
dn != d && // that is on list
dp.casNext(d, dn))) // d unspliced
casCleanMe(dp, null);
if(dp == pred) return; // s is already saved node
}else if(casCleanMe(null,pred))
return; // Postpone cleaning s
}
}
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
private static final long tailOffset;
private static final long cleanMeOffset;
static{
try{
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = TransferQueue.class;
headOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("head"));
tailOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("tail"));
cleanMeOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("cleanMe"));
}catch(Exception e){
throw new Error(e);
}
}
// TransferQueue的实现也比较复杂,但是底层同样不复杂【是CAS+Node节点来实现】。
}
// 只在构造函数中设置但不能声明为final而让序列化不会更加复杂。由于最多只在公共方法中访问一次,因此在使用volatile而不是final时没有明显的性能
损失。
private transient volatile Transferer transferer;
public SynchronousQueue(boolean fair) { // 默认为false
transferer = fair ? new TransferQueue() : new TransferStack();
}
public void put(E o) throws InterruptedException {
// 插入元素到队列,为其它线程接收元素,等待如有必要
if (o == null) throw new NullPointerException();if (transferer.transfer(o, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
// 插入元素到队列,为其它线程接收元素,等待如有必要,具有等待时限
public boolean offer(E o, long timeout, TimeUnit unit)throws InterruptedException {
if(o == null) throw new NullPointerException();
if(transferer.transfer(o, true, unit.toNanos(timeout)) != null)
return true;
if(!Thread.interrupted())
return false;
throw new InterruptedException();
}
public boolean offer(E e) { // 插入元素到队列,为其它线程接收元素,等待如有必要
if(e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
public E take() throws InterruptedException {
// 检索和移除队列头节点,为其它线程插入,等待如有必要
Object e = transferer.transfer(null, false, 0);if (e != null) return (E)e;
Thread.interrupted();
throw new InterruptedException();
}
// 检索和移除队列头节点,为其它线程插入,等待如有必要,具有等待时限
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
Object e = transferer.transfer(null, true, unit.toNanos(timeout));
if(e != null || !Thread.interrupted())
return (E)e;
throw new InterruptedException();
}
public int drainTo(Collection<? super E> c, int maxElements) { // 将队列中的元素复制到集合中
if(c == null) throw new NullPointerException();
if(c == this) throw new IllegalArgumentException();
int n = 0;
E e;
// 同drainTo(Collection<? super E> c)的实现差异在于:n < maxElements的判断
while(n < maxElements && (e = poll()) != null) {
c.add(e);
++n;
}
return n;
}
// SynchronousQueue提供的方法的实现是Transferer.transferer()方法的灵活运用
}
实现总结:SynchronousQueue内部维护了Transferer对象,可根据SynchronousQueue(boolean fair)构造函数对公平性的设置,Transferer可初始化为TransferQueue【true】,TransferStack【默认false】,SynchronousQueue所提供的所有方法的实现【插入和获取元素】几乎全部交由Transferer的transferer(Object e, boolean timed, long nanos)方法实现。Transferer的底层由LockSupport,CAS和内置的Node来实现的,通过CAS Head节点来实现获取或者插入元素。
阅读全文
0 0
- JDK7中SynchronousQueue源码解析
- JDK7中LockSupport源码解析
- JDK7中AtomicInteger源码解析
- JDK7中ArrayBlockingQueue源码解析
- JDK7中LinkedBlockingQueue源码解析
- JDK7中StringBuffer/StringBuilder源码解析
- JDK7中ReentrantLock源码解析(1)
- JDK7中ReentrantLock源码解析(2)
- JDK7中ReentrantLock源码解析(3)
- JDK7中ReentrantReadWriteLock源码解析(1)
- JDK7中ReentrantReadWriteLock源码解析(2)
- JDK7中ReentrantReadWriteLock源码解析(3)
- JDK7中TransferQueue的使用以及TransferQueue与SynchronousQueue的差别
- JDK7中Condition源码概述
- JDK7中Lock源码概述
- JDK7中ReadWriteLock源码概述
- JDK7中Executor源码概述
- JDK7中ExecutorService源码概述
- redis数据库1-基础
- java异常体系
- 反射机制的概述和字节码对象的获取,通过反射获取构造方法并使用;通过反射获取成员变量并使用;通过反射获取私有成员变量并使用;通过反射获取成员方法并使用
- native与js交互(WKWebView )
- somCNS短信接口替换
- JDK7中SynchronousQueue源码解析
- Mybatis配置文件解析过程详解
- mybatis里使用JSONArray.fromObject()报错
- MXnet代码实战之多层感知机
- OpenCV boundingRect 与 boundingRect 用法
- 4. ubuntu php composer
- mongo常用命令
- javaBean的概述和规范;BeanUtils的概述;BeanUtils的常用方法:获取类的字节码文件;通过类的构造方法获取成员变量;暴力访问 setAccessible(true)
- mongotemplate mongodb的各种操作 模糊查询 精确查询 等等