SynchronousQueue

SynchronousQueue是一个典型的生产者与消费者队列,当一个线程A向队列中放入数据,除非其它线程将数据取走,否则线程A将一直阻塞。也就是说一个put操作渴望一个take操作,同样一个take操作渴望一个put操作。

同步队列分为公平与非公平模式,公平模式下遵循先进先出的原则基于队列结构。非公平模式则使用后进先出的方式,同栈结构。

同步队列实现的这两种模式,是通过实现内部接口abstract static class Transferer<E>来分别完成。

公平模式使用TransferQueue类实现,非公平模式使用TransferStack类实现,具体的处理逻辑在接口的

transfer(E e, boolean timed, long nanos)方法中完成。

transfer方法集put和take操作于一身,区别在于put:e是要放的元素,take:e是null。

public E take() throws InterruptedException {
        //调用接口Transferer实现类的transferer方法,null代表take操作
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }


public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        //调用接口Transferer实现类的transferer方法,传入e
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }

 

公平模式

基于队列,先进先出,从尾部添加,从头部取

E transfer(E e, boolean timed, long nanos) {
           
            QNode s = null; // constructed/reused as needed
            //是put操作(isData是true)还是take
            boolean isData = (e != null);

            for (;;) {
                //对于当前线程来说,t是尾节点,h是头节点
                QNode t = tail;
                QNode h = head;
                if (t == null || h == null)         // 不用考虑,只要new一个实例,t和h都会指向一个虚拟节点
                    continue;      
                //队列是空,或(当前操作类型与队列的节点操作类型相同)
                //队列最初先执行的put那么插入的节点都是put节点(isData是true),
                //反之最初执行的是take,那么插入的都是take节点即isData是false
                if (h == t || t.isData == isData) { // 空或相同模式
                    QNode tn = t.next;
                    //t最初已赋值为tail,如果这时不相等,必定是有其它线程往队列中
                    //插入的新节点,那么当前操作需要重新定位头尾节点,重新执行for
                    if (t != tail)                  // inconsistent read
                        continue;
                    //tn是t(也就是tail尾节点)的下一个节点,正常情况下应该是null
                    //如果不是null说明其它线程往队列里插入了新节点,如果另一个线程
                    //尾部插入新节点是还没来得及更新tail的引用,那么当前线程“帮忙”更新下,
                    //并重新执行for重新定位头尾节点
                    if (tn != null) {               // lagging tail
                        advanceTail(t, tn);
                        continue;
                    }
                    if (timed && nanos <= 0)        // can't wait
                        return null;
                    //第一次总是null只有下面t.casNext(null, s)失败重新循环是才无需再次new
                    if (s == null)
                        s = new QNode(e, isData);
                    //如果当前位节点的next没有被其它线程更新,则设置尾节点的下个节点为s
                    if (!t.casNext(null, s))        // failed to link in
                        continue;
                    //将tail指针更新为s节点
                    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的else分支执行advanceHead(h, m);LockSupport.unpark(m.waiter);后唤起当前线程并进入此if分支
                    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) ? (E)x : e;

                } else {                            // complementary-mode
                    //互斥模式:
                    //即队列中的节点isData是true(节点类型是put),那么当前必定是take操作
                    //相反队列中的节点isDate是false(节点类型是take),那么当前必定是put操作
                    QNode m = h.next;               // node to fulfill
                    //公平模式下遵循先进先出原则,如果此时t!=tail或m==null或h!=head
                    //说明另一个线程执行了插入或读取(删除)操作,那么这里自旋以便重新获取头尾节点
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read

                    Object x = m.item;
                    //另外一个线程已经抢先执行了相同操作,如当前是put(isData是true)
                    //另外一个线程抢先执行put此时x!=null是true,那么执行advanceHead删除一个节点(头节点后移一位)
                    if (isData == (x != null) ||    // m already fulfilled
                        //x与m相等说明执行了删除操作,即线程执行了interrupt
                        x == m ||                   // m cancelled
                        //没有设置成功,说明其它线程抢先“取走”了数据,同样的移动头结点
                        !m.casItem(x, e)) {         // lost CAS
                        advanceHead(h, m);          // dequeue and retry
                        //未成功对节点进行出队,再次循环尝试
                        continue;
                    }
                    //成功交换数据,移动头节点,并唤醒阻塞的线程,使其继续进行后续操作
                    //交换分2种情况:
                    //1. 上个线程w在take上因没有数据而阻塞,当前线程put数据后,w唤醒,w线程成功取到数据,得以继续
                    //2.上个线程在w上执行put操作,因为数据已有而使得put操作阻塞,当前线程执行take后将数据取走,并唤醒w,w线程恢复得以继续put
                    advanceHead(h, m);              // successfully fulfilled
                    LockSupport.unpark(m.waiter);
                    //当前线程take操作返回节点的item的值x,如果是put操作返回put的值本身e
                    return (x != null) ? (E)x : e;
                }
            }
        }

awaitFulfill方法:

Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
            /* Same idea as TransferStack.awaitFulfill */
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Thread w = Thread.currentThread();
            //如果当前队列只有这唯一的一个node那么需要进行自旋,自旋的目的是为了提高性能
            //,因为可能存在一个或多个消费者线程急需取数据,取操作在自旋时就已经完成,
            //有效避免了一次对当前线程挂起与恢复的开销,如果队列中有多个节点那无需
            //自旋,直接挂起当前线程
            int spins = ((head.next == s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            for (;;) {
                //当前线程是否调用过w.interrupt()唤醒操作
                if (w.isInterrupted())
                    //将当前节点s的item指向s即s.item=s
                    s.tryCancel(e);
                Object x = s.item;
                //3种情况会进入if直接返回x
                //1.被挂起的线程执行了interrupt方法
                //2.线程w因put挂起,另一个线程取走了与线程w关联的节点的数据(take操作)
                //3.线程w因take挂起,另一个线程往与线程w关联的节点进行了put操作
                if (x != e)
                    return x;
                if (timed) {
                    nanos = deadline - System.nanoTime();
                    //指定了超时时间,其put的值没有被消费,或没生产者导致take仍获取不到值
                    //其实就不满足x!=e,那么执行当前节点的tryCancel方法
                    if (nanos <= 0L) {
                        //执行s.item=s以便再次自旋时能够满足if (x != e)并退出当前方法
                        //最终执行clean方法删除节点
                        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);
            }
        }

put方法

//put方法属于阻塞操作,除非成功进行了put或调用了当前线程的interrupt方法
//否则不会有返回
public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        //执行put操作或take操作都调用同一个方法transfer,对于put调用transfer成功执行返回值是
        //要放入的元素e,对于take调用transfer返回值是另外一个线程put进去的值,因此正常情况下transfer返回值是非空的,只有对当前线程执行interrupt进行终止时才会返回null
        if (transferer.transfer(e, false, 0) == null) {
            //清除状态标识并手动抛异常,这也是为什么在InterruptedException的catch块中isInerrupted总返回false
            Thread.interrupted();
            throw new InterruptedException();
        }
    }

clean方法

 //pred是s的前一个节点,即pred.next等于s
 void clean(QNode pred, QNode s) {
            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
                //如果第一个节点已经删除即hn.item等于hn,那么移动head并重新自旋
                if (hn != null && hn.isCancelled()) {
                    //需要注意的是频繁删除尾节点可能会导致节点链中存在部分item自引用的节点
                    //这里也可以帮忙进一步清除
                    advanceHead(h, hn);
                    continue;
                }
                QNode t = tail;      // Ensure consistent read for tail
                if (t == h)//队列是空直接返回
                    return;
                QNode tn = t.next;
                //t不等于tail说明另一个线程修改了尾节点,重新自旋,再次定位头尾
                if (t != tail)
                    continue;
                //tn不等于null说明另一个线程在尾节点插入了新节点,如果尾节点指针还没更新,则帮忙更新下,继续自旋,重新定位头尾
                if (tn != null) {
                    advanceTail(t, tn);
                    continue;
                }
                //最好的情况下,删除的节点不是尾节点,只需将pre节点的next更新为要删除节点s的next
                if (s != t) {        // 非尾节点
                    QNode sn = s.next;
                    //sn等于s说明已经删除,否则更新并返回
                    if (sn == s || pred.casNext(s, sn))
                        return;
                }
                //如果清除的是尾节点:
                //cleanMe保持上次要清除的节点的pre
                //如果发现cleanMe是null,则将清除节点的pre赋值给它,然后什么都不做,等下次清理尾节点时发现cleanMe有值再执行清理操作
                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
                        //清理完成后将cleanMe设为null
                        casCleanMe(dp, null);
                    //相等说明当前pred已经保存到cleanMe,下次再清理,直接返回
                    if (dp == pred)
                        return;      // s is already saved node
                } else if (casCleanMe(null, pred))
                    //先保存要清理节点的前驱,以便下次执行清理,让后直接返回
                    //为什么尾节点不立即清除,因为清理尾节点需要更新tail指针到前一个节点
                    //而当前节点并没有前驱节点的引用,因此通过这种方式规避
                    return;          // Postpone cleaning s
            }
        }

非公平模式

基于栈,后进先出,从头部添加,再从头部取

 

E transfer(E e, boolean timed, long nanos) {
            //当前要插入的节点
            SNode s = null; // constructed/reused as needed
            int mode = (e == null) ? REQUEST : DATA;//take还是put

            for (;;) {
                SNode h = head;//第一次head是null
                //队列是空或模式相同为同模式,同模式会进行入队操作
                if (h == null || h.mode == mode) {  // empty or same-mode
                    //已经超时直接返回,顺便看看需不需要更新head
                    if (timed && nanos <= 0) {      // can't wait
                        if (h != null && h.isCancelled())
                            casHead(h, h.next);     // pop cancelled node
                        else
                            return null;
                    //创建一个新节点s(为null创建)s的netx是h,并将新节点设为head
                    //如果设置失败,再次自旋重试
                    } else if (casHead(h, s = snode(s, e, h, mode))) {
                        //将节点插入到开头后尝试挂起线程
                        SNode m = awaitFulfill(s, timed, nanos);
                        //线程恢复后看返回值m和s是否是同一个节点,如果是,说明
                        //节点s被取消了,要么执行了线程的interrupt要么操作超时
                        if (m == s) {               // wait was cancelled
                            clean(s);//尝试清除取消的节点
                            return null;
                        }
                        //此if成立的条件是:另一个线程在当前的else if分支中进行了“取操作”
                        //导致当前节点s已经和s之前的某个fulfilling节点进行了匹配,但还没
                        //来得及将s和匹配的fulfilling节点弹出,那么这里帮助另一个线程执行弹出
                        if ((h = head) != null && h.next == s)
                            casHead(h, s.next);     // help s's fulfiller
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    }
                } else if (!isFulfilling(h.mode)) { // try to fulfill
                    //相异模式,且只有当前线程在取(即头结点不是fulfilling模式)
                    //当前头节点已经删除,帮助其它线程将头结点后移(线程间的互助)
                    if (h.isCancelled())            // already cancelled
                        casHead(h, h.next);         // pop and retry
                    //在开头插入一个FULFILLING模式的节点s(站位),head指向s
                    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
                            //已全部弹出,栈是空将head设为null
                            if (m == null) {        // all waiters are gone
                                casHead(s, null);   // pop fulfill node
                                //继续自旋重新并重新new一个s节点
                                s = null;           // use new node next time
                                break;              // restart main loop
                            }
                            SNode mn = m.next;
                            //将当前节点m与前一个fulfilling节点s相匹配,并恢复节点m的线程
                            if (m.tryMatch(s)) {
                                //成功匹配弹出(m和s),将头节点指向后面的节点mn
                                casHead(s, mn);     // pop both s and m
                                //匹配成返回相关值take取m.item,put返回放入的值
                                return (E) ((mode == REQUEST) ? m.item : s.item);
                            } else                  // lost match
                                //匹配失败说明另一行线程已经匹配,如果另一个线程还没来得及
                                //执行弹出,这里帮忙弹一下,并将s(fulfilling)的next指向mn
                                s.casNext(m, mn);   // help unlink
                        }
                    }
                } else {                            // help a fulfiller
                    //相异模式
                    //头节点是fulfilling模式,说明有节点正在匹配(正在取操作)
                    //那么执行助推操作后自旋完成当前操作流程
                    SNode m = h.next;               // m is h's match
                    //已全部弹出,栈是空将head设为null
                    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
                    }
                }
            }
        }

boolean tryMatch(SNode s) {
                //当前节点的match属性是空则将match属性设置为s(s为fulfilling类型节点)
                if (match == null &&
                    UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
                    Thread w = waiter;
                    //准备恢复当前相关(put/take)操作的线程
                    if (w != null) {    // waiters need at most one unpark
                        waiter = null;
                        LockSupport.unpark(w);
                    }
                    return true;
                }
                return match == s;
            }

static SNode snode(SNode s, Object e, SNode next, int mode) {
            if (s == null) s = new SNode(e);
            s.mode = mode;
            s.next = next;
            return s;
        }

SNode awaitFulfill(SNode s, boolean timed, long nanos) {
            
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Thread w = Thread.currentThread();
            //如果当前队列是空或者s就是头节点或者头结点是isFulfilling模式(有线程正在取数据),那么可以自旋一段时间,因为在自旋的周期内
            //其他线程可以取走数据,避免了一次无意义的线程阻塞唤醒操作
            int spins = (shouldSpin(s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            for (;;) {
                if (w.isInterrupted())
                    //如果当前线程被打断则取消节点(属性match赋值为this自己;)
                    //紧接着下面的if会成功执行退出,最后执行clean(s);
                    s.tryCancel();
                SNode m = s.match;
                if (m != null)
                    //match不为空,要么s被取消,要么s和前面的fulfilling节点匹配(其它线程进行了相反操作,如take)
                    return m;
                if (timed) {
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) {
                        //操作超时则取消节点
                        //如put操作,10秒后还没有进行take,将队列中的当前节点取消
                        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);
            }
        }

 /**
  *清除节点
  */
 void clean(SNode s) {
            //清除属性
            s.item = null;   // forget item
            s.waiter = null; // forget thread

            SNode past = s.next;
            if (past != null && past.isCancelled())
                //如果当前节点s的下个节点已经取消则跳过取消的节点
                past = past.next;

            // Absorb cancelled nodes at head
            SNode p;
            //如果头部节点已经取消,则更新头部指针
            while ((p = head) != null && p != past && p.isCancelled())
                casHead(p, p.next);

            // Unsplice embedded nodes
            //到这里p指向第一个非取消的节点
            while (p != null && p != past) {
                //这个循环不仅仅只是清理s,而是清理栈上所有已取消的节点
                SNode n = p.next;
                if (n != null && n.isCancelled())
                    //如果p的下个节点已经取消,那么更新p的next指针
                    p.casNext(n, n.next);
                else
                    p = n;
            }
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值