简介

SynchronousQueue是一种特殊的阻塞队列,该队列没有容量。

【存数据线程】到达队列后,若发现没有【取数据线程】在此等待,则【存数据线程】便入队等待,直到有【取数据线程】来取数据,并释放【存数据线程】;

同理,【取数据线程】到达队列后,若发现没有【存数据线程】在此等待,则【取数据线程】便入队等待,直到有【存数据线程】来存数据,并释放【取数据线程】。

公平模式,由伪栈实现,TransferStack

公平模式,由伪队列实现,TransferQueue

源码解析

Transferer

  1. abstract static class Transferer<E> {
  2. abstract E transfer(E e, boolean timed, long nanos); // 实现一个put或者take操作
  3. }

TransferStack

非公平模式

属性

  1. static final int REQUEST = 0; // 未得到数据的消费者
  2. static final int DATA = 1; // 未交出数据的生产者
  3. static final int FULFILLING = 2; // 正在与另外一个消费者/生产者匹配
  4. volatile SNode head; // stack的头结点

内部类SNode

属性

  1. volatile SNode next; // 指向下一个结点
  2. volatile SNode match; // 与当前结点匹配的结点
  3. volatile Thread waiter; // 记录当前线程
  4. Object item; // 数据,对于消费者,为空
  5. int mode; // 模式,取值:REQUEST, DATA, FULFILLING

构造方法

  1. SNode(Object item) {
  2. this.item = item;
  3. }

关键方法

  1. boolean casNext(SNode cmp, SNode val) { // CAS next属性,cmp -> val
  2. return cmp == next && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
  3. }
  4.  
  5. boolean tryMatch(SNode s) { // 尝试匹配结点s和当前结点
  6. if (match == null && UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) { // 如果匹配成功
  7. Thread w = waiter;
  8. if (w != null) {
  9. waiter = null;
  10. LockSupport.unpark(w); // 唤醒阻塞的线程
  11. }
  12. return true;
  13. }
  14. return match == s;
  15. }
  16.  
  17. void tryCancel() { // CAS match属性,null -> this
  18. UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
  19. }
  20.  
  21. boolean isCancelled() { // 判断此结点是否已经被取消
  22. return match == this;
  23. }

关键方法

  1. static boolean isFulfilling(int m) { // 查看结点是否处于匹配模式
  2. return (m & FULFILLING) != 0;
  3. }
  4.  
  5. boolean casHead(SNode h, SNode nh) { // CAS head属性,h -> nh
  6. return h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
  7. }
  8.  
  9. static SNode snode(SNode s, Object e, SNode next, int mode) { // 创建一个新的结点,或者重置s结点的相关域
  10. if (s == null)
  11. s = new SNode(e);
  12. s.mode = mode;
  13. s.next = next;
  14. return s;
  15. }
  16.  
  17. boolean shouldSpin(SNode s) { // 是否应该自旋等待
  18. SNode h = head;
  19. return (h == s || h == null || isFulfilling(h.mode));
  20. }

transfer()

基本的算法是在一个无限循环中,每次执行下面三种情况的其中一种:

  1. 如果当前栈为空,或者已经包含与当前结点模式相同的结点,尝试入栈,并一直等待,直到遇到与之匹配(模式互补)的结点前来将其唤醒,并返回结果:a. 如果被取消,则返回null; b.如果当前结点是消费者,则返回匹配结点的数据;c. 如果当前结点是生产者,则返回当前结点的数据。
  2. 如果栈中包含与当前结点模式互补的结点,则设置当前结点的模式为FULFILLING,并尝试入栈,和对应的结点互相匹配,完成后,双双出栈,并返回生产者的数据。
  3. 如果栈顶结点的模式是FULFILLING,说明此刻有结点正在配对,当前线程帮助它们配对和弹出栈,然后在处理自己的事情,继续循环执行相应的操作。
  1. E transfer(E e, boolean timed, long nanos) {
  2. SNode s = null;
  3. int mode = (e == null) ? REQUEST : DATA; // 当前结点的模式
  4. for (;;) {
  5. SNode h = head;
  6. if (h == null || h.mode == mode) { // 栈为空,或者栈顶结点与当前结点的模式相同
  7. if (timed && nanos <= 0) { // 超时
  8. if (h != null && h.isCancelled())
  9. casHead(h, h.next); // 弹出已经取消的结点
  10. else
  11. return null;
  12. } else if (casHead(h, s = snode(s, e, h, mode))) { // 构建结点入栈
  13. SNode m = awaitFulfill(s, timed, nanos); // 在此等待匹配结点唤醒自己
  14. if (m == s) { // 已取消
  15. clean(s); // 清理工作
  16. return null;
  17. }
  18. if ((h = head) != null && h.next == s)
  19. casHead(h, s.next); // 帮助配对成功的结点出栈
  20. return (E) ((mode == REQUEST) ? m.item : s.item); // 返回生产者的数据
  21. }
  22. } else if (!isFulfilling(h.mode)) { // 尝试匹配结点
  23. if (h.isCancelled()) // 已经被取消
  24. casHead(h, h.next); // 更新head
  25. else if (casHead(h, s = snode(s, e, h, FULFILLING | mode))) { // 设置为FULFILLING模式入栈
  26. for (;;) { // 寻找匹配者
  27. SNode m = s.next; // m是s的匹配者
  28. if (m == null) { // 为空
  29. casHead(s, null); // 弹出已经配对的结点
  30. s = null; // 下一次重新构造结点
  31. break; // 重新开始
  32. }
  33. SNode mn = m.next;
  34. if (m.tryMatch(s)) {
  35. casHead(s, mn); // 弹出s和m
  36. return (E) ((mode == REQUEST) ? m.item : s.item); // 返回生产者的数据
  37. } else // 失败
  38. s.casNext(m, mn); // 剔除m
  39. }
  40. }
  41. } else {
  42. SNode m = h.next; // m是h的匹配者
  43. if (m == null) // 为空
  44. casHead(h, null); // 弹出已经配对的结点
  45. else {
  46. SNode mn = m.next;
  47. if (m.tryMatch(h)) // 帮助完成匹配
  48. casHead(h, mn); // 弹出h和m
  49. else // 失败
  50. h.casNext(m, mn); // 剔除m
  51. }
  52. }
  53. }
  54. }

awaitFulfill()

在线程阻塞之前,设置到结点的waiter域,并且检查一次线程线程的中断状态,若中断则取消。如果执行结点处于栈顶,阻塞之前会自旋一会儿,说不定马上就有结点来匹配,这样就不用阻塞了。主循环检查顺序: 中断优先于正常返回,正常返回优先于超时。

  1. SNode awaitFulfill(SNode s, boolean timed, long nanos) {
  2. final long deadline = timed ? System.nanoTime() + nanos : 0L; // 超时时间点
  3. Thread w = Thread.currentThread(); // 当前线程
  4. int spins = (shouldSpin(s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0); // 自旋次数
  5. for (;;) {
  6. if (w.isInterrupted()) // 检查中断,若中断,则取消此结点
  7. s.tryCancel();
  8. SNode m = s.match;
  9. if (m != null) // 正常返回
  10. return m;
  11. if (timed) { // 检查超时
  12. nanos = deadline - System.nanoTime();
  13. if (nanos <= 0L) { // 若超时,取消此结点
  14. s.tryCancel();
  15. continue;
  16. }
  17. }
  18. if (spins > 0) // 自旋
  19. spins = shouldSpin(s) ? (spins - 1) : 0;
  20. else if (s.waiter == null)
  21. s.waiter = w; // 记录线程
  22. else if (!timed)
  23. LockSupport.park(this); // 阻塞线程
  24. else if (nanos > spinForTimeoutThreshold)
  25. LockSupport.parkNanos(this, nanos); // 带超时的阻塞
  26. }
  27. }

clean()

清理分3步

  1. 清理s结点,并判断s的next结点past,如果past也取消了,则跳过此结点,使得past变量指向下一个结点,到此为止,此为清理工作的最大深度
  2. 从head结点开始,依次跳过已经取消的结点,直到遇到未取消的结点(或者遇到past结点,或为空),重新设置head结点为p结点
  3. 从p到past结点,清理掉所有已经取消的结点
  1. void clean(SNode s) {
  2. s.item = null; // 清理s结点
  3. s.waiter = null;
  4.  
  5. SNode past = s.next; // s的下一个结点past
  6. if (past != null && past.isCancelled()) // 如果past也取消了,则直接跳到past的下一个结点
  7. past = past.next;
  8.  
  9. SNode p;
  10. while ((p = head) != null && p != past && p.isCancelled()) // 从head结点开始,遍历清理已经取消的结点,直到遇到没有被取消的结点,并设置为新的head结点
  11. casHead(p, p.next);
  12.  
  13. while (p != null && p != past) { // 从p结点到past结点(但不包括past), 遍历清理所有已经取消的结点
  14. SNode n = p.next;
  15. if (n != null && n.isCancelled())
  16. p.casNext(n, n.next);
  17. else
  18. p = n;
  19. }
  20. }

TransferQueue

公平模式

内部类QNode

属性

  1. volatile QNode next; // 指向下一个结点
  2. volatile Object item; // 存放数据,isData为false时,该节点为null, 为true时,匹配后,该节点会置为null
  3. volatile Thread waiter; // 控制线程的park/unpark
  4. final boolean isData; // 表示该结点是存数据还是取数据

关键方法

  1. QNode(Object item, boolean isData) { // 构造方法
  2. this.item = item;
  3. this.isData = isData;
  4. }
  5.  
  6. boolean casNext(QNode cmp, QNode val) { // CAS next域 cmp -> val
  7. return next == cmp && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
  8. }
  9.  
  10. boolean casItem(Object cmp, Object val) { // CAS offset域 cmp -> val
  11. return item == cmp && UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
  12. }
  13.  
  14. void tryCancel(Object cmp) { // 取消结点
  15. UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
  16. }
  17.  
  18. boolean isCancelled() { // 检查是否已取消
  19. return item == this;
  20. }
  21.  
  22. boolean isOffList() { // 检查结点是否离开了队列
  23. return next == this;
  24. }

属性

  1. transient volatile QNode head; // 头结点
  2. transient volatile QNode tail; // 尾结点
  3. transient volatile QNode cleanMe; // 当一个结点被标记取消时,恰巧又是最后也给结点,那么将cleanMe作为该结点的predecessor

关键方法

  1. TransferQueue() { // 构造方法
  2. QNode h = new QNode(null, false); // 初始化 dummy node.
  3. head = h;
  4. tail = h;
  5. }
  6.  
  7. void advanceHead(QNode h, QNode nh) { // CAS head域,h -> nh
  8. if (h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
  9. h.next = h;
  10. }
  11.  
  12. void advanceTail(QNode t, QNode nt) { // CAS tail域,t -> nt
  13. if (tail == t)
  14. UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
  15. }
  16.  
  17. boolean casCleanMe(QNode cmp, QNode val) { // CAS cleanMe域,cmp -> val
  18. return cleanMe == cmp && UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
  19. }

transfer()

基本的算法是在一个无限循环中,每次执行下面两种情况的其中一种:

  1. 如果当前队列为空,或者已经与当前结点模式相同的结点,尝试入队,并一直等待,直到遇到与之匹配(模式互补)的结点前来将其唤醒,并返回匹配结点的数据。
  2. 如果队列中包含与当前结点模式互补的结点,则尝试和对应的结点互相匹配,完成后,将等待结点出队,并返回匹配结点的数据。
  3. 在每个动作里面,都会检测并帮助其他线程来完成节点推进。
  1. E transfer(E e, boolean timed, long nanos) {
  2. QNode s = null;
  3. boolean isData = (e != null);
  4.  
  5. for (;;) {
  6. QNode t = tail;
  7. QNode h = head;
  8. if (t == null || h == null) // 当前线程看到未初始化的头尾结点
  9. continue; // 自旋
  10.  
  11. if (h == t || t.isData == isData) { // 队列为空,或者包含相同模式的结点
  12. QNode tn = t.next;
  13. if (t != tail) // 过期数据
  14. continue;
  15. if (tn != null) { // 别的线程添加了新的结点,帮助更新tail域
  16. advanceTail(t, tn);
  17. continue;
  18. }
  19. if (timed && nanos <= 0) // 超时
  20. return null;
  21. if (s == null)
  22. s = new QNode(e, isData); // 构造结点
  23. if (!t.casNext(null, s)) // 连接失败
  24. continue;
  25.  
  26. advanceTail(t, s); // 设置s为tail结点
  27. Object x = awaitFulfill(s, e, timed, nanos); // 等待匹配
  28. if (x == s) { // 如果取消,清理结点
  29. clean(t, s);
  30. return null;
  31. }
  32.  
  33. if (!s.isOffList()) { // 如果s未离队
  34. advanceHead(t, s); // 设置s为head结点
  35. if (x != null)
  36. s.item = s;
  37. s.waiter = null;
  38. }
  39. return (x != null) ? (E) x : e;
  40.  
  41. } else { // 互补模式
  42. QNode m = h.next;
  43. if (t != tail || m == null || h != head)
  44. continue; // 读取的是过期的值,继续循环
  45.  
  46. Object x = m.item;
  47. if (isData == (x != null) || // m已经被匹配了
  48. x == m || // m被取消
  49. !m.casItem(x, e)) { // CAS失败
  50. advanceHead(h, m); // h出队,m设置为head结点,重来
  51. continue;
  52. }
  53.  
  54. advanceHead(h, m); // 成功,推进头节点
  55. LockSupport.unpark(m.waiter); // 唤醒等到线程
  56. return (x != null) ? (E) x : e;
  57. }
  58. }
  59. }

awaitFulfill()

  1. Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
  2. final long deadline = timed ? System.nanoTime() + nanos : 0L;
  3. Thread w = Thread.currentThread();
  4. int spins = ((head.next == s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0);
  5. for (;;) {
  6. if (w.isInterrupted())
  7. s.tryCancel(e);
  8. Object x = s.item;
  9. if (x != e)
  10. return x;
  11. if (timed) {
  12. nanos = deadline - System.nanoTime();
  13. if (nanos <= 0L) {
  14. s.tryCancel(e);
  15. continue;
  16. }
  17. }
  18. if (spins > 0)
  19. --spins;
  20. else if (s.waiter == null)
  21. s.waiter = w;
  22. else if (!timed)
  23. LockSupport.park(this);
  24. else if (nanos > spinForTimeoutThreshold)
  25. LockSupport.parkNanos(this, nanos);
  26. }
  27. }

同TransferStack.

clean()

在任意时间点,只有最后入队的结点不能立即删除,因为考虑到无所并发,线程争用下沉到CPU指令级别(CAS),最后入队的结点同时会有CAS Tail的动作,所以不能同一时间点,对同一个元素有多个CAS操作,因此,如果是最后入队的结点,可以将删除操作滞后。先将cleanMe结点的next域指向改结点,等到有新的结点入队时,再考虑删除上一版本的结点,此时,已满足条件。

  1. void clean(QNode pred, QNode s) {
  2. s.waiter = null; // 置空waiter域
  3. while (pred.next == s) {
  4. QNode h = head;
  5. QNode hn = h.next;
  6. if (hn != null && hn.isCancelled()) {
  7. advanceHead(h, hn); // 推进head结点
  8. continue;
  9. }
  10. QNode t = tail;
  11. if (t == h)
  12. return;
  13. QNode tn = t.next;
  14. if (t != tail)
  15. continue;
  16. if (tn != null) {
  17. advanceTail(t, tn);
  18. continue;
  19. }
  20. if (s != t) { // 如果s不是尾结点,直接将其删除
  21. QNode sn = s.next;
  22. if (sn == s || pred.casNext(s, sn))
  23. return;
  24. }
  25. QNode dp = cleanMe;
  26. if (dp != null) { // 尝试删除前一版本取消的结点,借助cleanMe结点
  27. QNode d = dp.next;
  28. QNode dn;
  29. if (d == null || // d已经被删除
  30. d == dp || // d已经出
  31. !d.isCancelled() || // d没被取消
  32. (d != t && // d not tail and
  33. (dn = d.next) != null && // 有后继结点
  34. dn != d && dp.casNext(d, dn))) // 删除d
  35. casCleanMe(dp, null);
  36. if (dp == pred)
  37. return;
  38. } else if (casCleanMe(null, pred))
  39. return;
  40. }
  41. }

行文至此结束。

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_sq.html

【JUC源码解析】SynchronousQueue的更多相关文章

  1. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  2. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  3. 【JUC源码解析】DelayQueue

    简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...

  4. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

  5. 【JUC源码解析】ConcurrentLinkedQueue

    简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...

  6. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  7. [源码]解析 SynchronousQueue 上界,下界.. 数据保存和数据传递. 堵塞队列. 有无频繁await?

     简析SynchronousQueue.LinkedBlockingQueue(两个locker,更快),ArrayBlockingQueue(一个locker,读写都竞争)     三者都是bloc ...

  8. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  9. Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue

    功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...

随机推荐

  1. 用于mask遮罩效果的图片配合resizableImage使用

    用于mask遮罩效果的图片配合resizableImage使用 效果: 作为素材用的图片: 源码: // // ViewController.m // Rect // // Created by Yo ...

  2. lua调用c++函数返回值作用

    2015/05/28 lua调用c++接口,返回给lua函数的是压入栈的内容,可以有多个返回值.但是c++接口本身也是有返回值的,这个返回值也非常的重要,会决定最后返回到lua函数的值的个数. (1) ...

  3. Linux ss命令详解

    ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息 ...

  4. 如何避免HBase写入过快引起的各种问题

    首先我们简单回顾下整个写入流程 client api ==> RPC ==> server IPC ==> RPC queue ==> RPC handler ==> w ...

  5. ZT C语言实现字符串倒序

    http://blog.chinaunix.net/uid-20788517-id-34777.html 分类: C/C++ 1 #include <stdio.h>  2 #includ ...

  6. Servlet 启动顺序

    在servlet的配置当中,<load-on-startup>1</load-on-startup>的含义是: 标记容器是否在启动的时候就加载这个servlet. 当值为0或者 ...

  7. hihocoder Round #c1(hihoCoder太阁最新面经算法竞赛1 )

    Test链接:https://cn.vjudge.net/contest/231849 选自hihoCoder太阁最新面经算法竞赛1 更多Test:传送门 A:区间求差 给一组区间集合A和区间集合B, ...

  8. CGI编程学习

    @CGI编程学习 目录(?)[+] 一.基本原理 CGI:通用网关接口(Common Gateway Interface)是一个Web服务器主机提供信息服务的标准接口.通过CGI接口,Web服务器就能 ...

  9. 写了一个web使用向导的小插件

    运行效果: 引入插件: <link rel="stylesheet" href="ez-guide.css"> <script src=&qu ...

  10. #leetcode刷题之路49-字母异位词分组

    给定一个字符串数组,将字母异位词组合在一起.字母异位词指字母相同,但排列不同的字符串.示例:输入: ["eat", "tea", "tan" ...