SynchronousQueue

SynchronousQueue 能解决什么问题?什么时候使用 SynchronousQueue?

  1. 1SynchronousQueue 没有任何内部容量。
  2. 2SynchronousQueue 中每一个插入操作必须等待一个与之配对的删除操作,反之亦然。
  3. 3SynchronousQueue 非常适合于线程之间交换信息,均衡生产者与消费者的处理速率。
  4. 4SynchronousQueue 的性能和吞吐量高于 LinkedBlockingQueue ArrayBlockingQueue

如何使用 SynchronousQueue?

  1. 1Executors.newCachedThreadPool() 使用 SynchronousQueue 作为其任务队列。
  2. 2)需要均衡生产和消费速率时,可以使用 SynchronousQueue

使用 SynchronousQueue 有什么风险?

  1. 1)生产速率持续高于消费速率时,会导致生产者阻塞,服务不可用。

SynchronousQueue 核心操作的实现原理?

  • 创建实例
  1. /**
  2. * Shared internal API for dual stacks and queues.
  3. */
  4. abstract static class Transferer<E> {
  5. /**
  6. * 执行一个 put 或 take 操作
  7. *
  8. * @param e 1)值为 null,则表示这是一个 take 操作
  9. * 2)值不为 null,则表示这是一个 put 操作
  10. * @param timed 操作的超时时间
  11. * @param nanos 以纳秒为单位
  12. * @return 1)值为 null 表示超时或线程被中断
  13. * 2)值不为 null,表示插入或读取的目标元素
  14. */
  15. abstract E transfer(E e, boolean timed, long nanos);
  16. }
  17. /**
  18. * 超时等待前的自旋次数
  19. */
  20. static final int MAX_TIMED_SPINS =
  21. Runtime.getRuntime().availableProcessors() < 2 ? 0 : 32;
  22. /**
  23. * 阻塞等待前的自旋次数
  24. */
  25. static final int MAX_UNTIMED_SPINS = SynchronousQueue.MAX_TIMED_SPINS * 16;
  26. /**
  27. * 超时时间小于 1000 纳秒时,自旋比阻塞等待效率高
  28. */
  29. static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
  30. /**
  31. * 转移接口
  32. */
  33. private transient volatile Transferer<E> transferer;
  34. /**
  35. * 创建一个非公平的同步队列
  36. */
  37. public SynchronousQueue() {
  38. this(false);
  39. }
  40. /**
  41. * fair==true,创建一个公平的同步队列,最先等待的最先释放
  42. * fair==false,创建一个非公平的同步队列
  43. */
  44. public SynchronousQueue(boolean fair) {
  45. transferer = fair ? new TransferQueue<>() : new TransferStack<>();
  46. }
  • 将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
  1. /**
  2. * 将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
  3. */
  4. @Override
  5. public void put(E e) throws InterruptedException {
  6. if (e == null) {
  7. throw new NullPointerException();
  8. }
  9. if (transferer.transfer(e, false, 0) == null) {
  10. Thread.interrupted();
  11. throw new InterruptedException();
  12. }
  13. }
  14. /** Dual stack */
  15. static final class TransferStack<E> extends Transferer<E> {
  16. /** 节点代表一个未完成的消费者 */
  17. static final int REQUEST = 0;
  18. /** 节点代表一个未完成的生产者 */
  19. static final int DATA = 1;
  20. /** 节点正在完成一个匹配的操作 */
  21. static final int FULFILLING = 2;
  22. /** Node class for TransferStacks. */
  23. static final class SNode {
  24. // 堆栈的下一个节点
  25. volatile SNode next;
  26. // 与当前节点匹配的对偶节点
  27. volatile SNode match;
  28. // 当前节点驻留的线程
  29. volatile Thread waiter;
  30. // 如果是数据节点则非空,如果是请求节点则为 null
  31. Object item;
  32. // 当前节点的状态
  33. int mode;
  34. SNode(Object item) {
  35. this.item = item;
  36. }
  37. /**
  38. * 参数原子更新 next 节点
  39. */
  40. boolean casNext(SNode cmp, SNode val) {
  41. return cmp == next &&
  42. SNode.SNEXT.compareAndSet(this, cmp, val);
  43. }
  44. /**
  45. * 尝试将目标节点匹配给当前节点,如果成功则唤醒驻留在当前节点的线程
  46. */
  47. boolean tryMatch(SNode s) {
  48. // 尝试设置匹配节点为 s
  49. if (match == null &&
  50. SNode.SMATCH.compareAndSet(this, null, s)) {
  51. // 读取驻留线程
  52. final Thread w = waiter;
  53. if (w != null) { // waiters need at most one unpark
  54. waiter = null;
  55. // 唤醒驻留线程
  56. LockSupport.unpark(w);
  57. }
  58. // 匹配成功返回 true
  59. return true;
  60. }
  61. return match == s;
  62. }
  63. /**
  64. * 将匹配节点设置为 this 表示节点被删除
  65. */
  66. void tryCancel() {
  67. SNode.SMATCH.compareAndSet(this, null, this);
  68. }
  69. /**
  70. * 当前节点已经被取消
  71. * created by ZXD at 8 Dec 2018 T 11:11:25
  72. */
  73. boolean isCancelled() {
  74. return match == this;
  75. }
  76. // VarHandle mechanics
  77. private static final VarHandle SMATCH;
  78. private static final VarHandle SNEXT;
  79. static {
  80. try {
  81. final MethodHandles.Lookup l = MethodHandles.lookup();
  82. SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
  83. SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
  84. } catch (final ReflectiveOperationException e) {
  85. throw new Error(e);
  86. }
  87. }
  88. }
  89. /** 堆栈顶部元素 */
  90. volatile SNode head;
  91. /**
  92. * Puts or takes an item.
  93. */
  94. @Override
  95. @SuppressWarnings("unchecked")
  96. E transfer(E e, boolean timed, long nanos) {
  97. /**
  98. * 1)如果堆栈为空或包含相同类型的节点,则新建节点并入栈,阻塞等待。
  99. * 2)如果堆栈中包含对偶节点,则将一个对偶节点入栈,
  100. * 并同时弹出新增节点及与其匹配的节点。
  101. * 3)如果堆栈顶部已经持有对偶节点,则帮助它们弹出堆栈。
  102. */
  103. SNode s = null; // constructed/reused as needed
  104. // 计算节点模式
  105. final int mode = e == null ? TransferStack.REQUEST : TransferStack.DATA;
  106. for (;;) {
  107. // 读取栈顶节点
  108. SNode h = head;
  109. // 1)堆栈为空或栈顶元素和新增节点模式一致
  110. if (h == null || h.mode == mode) { // empty or same-mode
  111. // 1-1)如果设置了超时时间,并且已经超时
  112. if (timed && nanos <= 0L) { // can't wait
  113. // 头节点不为 null && 头节点已经被取消
  114. if (h != null && h.isCancelled()) {
  115. // 尝试原子更新栈顶节点
  116. casHead(h, h.next); // pop cancelled node
  117. } else {
  118. // 删除栈顶后返回 null
  119. return null;
  120. }
  121. // 1-2)尝试将新节点入栈
  122. } else if (casHead(h, s = TransferStack.snode(s, e, h, mode))) {
  123. // 尝试在指定的超时时间内等待对偶节点
  124. final SNode m = awaitFulfill(s, timed, nanos);
  125. // 线程超时或被其他线程中断
  126. if (m == s) { // wait was cancelled
  127. // 清除新增节点
  128. clean(s);
  129. return null;
  130. }
  131. // 当前节点和对偶节点位于栈顶前两个位置,则弹出它们
  132. if ((h = head) != null && h.next == s)
  133. {
  134. casHead(h, s.next); // help s's fulfiller
  135. }
  136. /**
  137. * 1)新值的是请求节点,则返回对偶节点的数据值
  138. * 2)新值的是数据节点,则返回其持有的数据
  139. */
  140. return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
  141. }
  142. // 2)头节点不是 fulFill 节点
  143. } else if (!TransferStack.isFulfilling(h.mode)) { // try to fulfill
  144. // 1)头节点已经被取消
  145. if (h.isCancelled()) {
  146. // 则重新写入头结点
  147. casHead(h, h.next); // pop and retry
  148. // 2)尝试写入一个 fulFill 节点到栈顶
  149. } else if (casHead(h, s=TransferStack.snode(s, e, h, TransferStack.FULFILLING|mode))) {
  150. for (;;) { // loop until matched or waiters disappear
  151. // 读取匹配节点
  152. final SNode m = s.next; // m is s's match
  153. // 已经不存在等待节点【被其他线程帮助弹出了】
  154. if (m == null) { // all waiters are gone
  155. // 写入头节点
  156. casHead(s, null); // pop fulfill node
  157. s = null; // use new node next time
  158. break; // restart main loop
  159. }
  160. // 读取匹配节点的后置节点
  161. final SNode mn = m.next;
  162. // 如果仍处于匹配模式
  163. if (m.tryMatch(s)) {
  164. // 同时弹出这两个节点
  165. casHead(s, mn); // pop both s and m
  166. /**
  167. * 1)新值的是请求节点,则返回对偶节点的数据值
  168. * 2)新值的是数据节点,则返回其持有的数据
  169. */
  170. return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
  171. }
  172. else {
  173. s.casNext(m, mn); // help unlink
  174. }
  175. }
  176. }
  177. // 3)头结点是一个 fulFill 节点,则帮助将它和它的对偶节点出栈
  178. } else { // help a fulfiller
  179. final SNode m = h.next; // m is h's match
  180. if (m == null) {
  181. casHead(h, null); // pop fulfilling node
  182. } else {
  183. final SNode mn = m.next;
  184. if (m.tryMatch(h)) {
  185. casHead(h, mn); // pop both h and m
  186. }
  187. else {
  188. h.casNext(m, mn); // help unlink
  189. }
  190. }
  191. }
  192. }
  193. }
  194. /**
  195. * 尝试原子更新头结点
  196. * created by ZXD at 8 Dec 2018 T 10:56:47
  197. * @param h
  198. * @param nh
  199. * @return
  200. */
  201. boolean casHead(SNode h, SNode nh) {
  202. return h == head &&
  203. TransferStack.SHEAD.compareAndSet(this, h, nh);
  204. }
  205. /**
  206. * 将节点加入栈顶
  207. */
  208. static SNode snode(SNode s, Object e, SNode next, int mode) {
  209. // 如果是新增节点
  210. if (s == null) {
  211. // 则创建节点
  212. s = new SNode(e);
  213. }
  214. // 写入模式
  215. s.mode = mode;
  216. // 写入后置节点
  217. s.next = next;
  218. return s;
  219. }
  220. /**
  221. * 线程执行自旋或阻塞等待,直到堆栈中写入一个对偶节点、线程超时、被其他线程中断
  222. *
  223. * @param s 等待节点
  224. * @param timed 是否是超时模式
  225. * @param nanos 超时纳秒
  226. * @return 1)出现对偶节点,则返回对偶节点
  227. * 2)线程超时或被中断,则返回 s
  228. */
  229. SNode awaitFulfill(SNode s, boolean timed, long nanos) {
  230. /*
  231. * When a node/thread is about to block, it sets its waiter
  232. * field and then rechecks state at least one more time
  233. * before actually parking, thus covering race vs
  234. * fulfiller noticing that waiter is non-null so should be
  235. * woken.
  236. *
  237. * When invoked by nodes that appear at the point of call
  238. * to be at the head of the stack, calls to park are
  239. * preceded by spins to avoid blocking when producers and
  240. * consumers are arriving very close in time. This can
  241. * happen enough to bother only on multiprocessors.
  242. *
  243. * The order of checks for returning out of main loop
  244. * reflects fact that interrupts have precedence over
  245. * normal returns, which have precedence over
  246. * timeouts. (So, on timeout, one last check for match is
  247. * done before giving up.) Except that calls from untimed
  248. * SynchronousQueue.{poll/offer} don't check interrupts
  249. * and don't wait at all, so are trapped in transfer
  250. * method rather than calling awaitFulfill.
  251. */
  252. // 计算截止时间
  253. final long deadline = timed ? System.nanoTime() + nanos : 0L;
  254. // 读取当前线程
  255. final Thread w = Thread.currentThread();
  256. // 计算自旋次数
  257. int spins = shouldSpin(s)
  258. ? timed ? SynchronousQueue.MAX_TIMED_SPINS : SynchronousQueue.MAX_UNTIMED_SPINS
  259. : 0;
  260. for (;;) {
  261. // 线程已经被中断
  262. if (w.isInterrupted()) {
  263. // 则尝试删除节点
  264. s.tryCancel();
  265. }
  266. // 读取匹配节点
  267. final SNode m = s.match;
  268. /**
  269. * 如果存在匹配节点,则返回它
  270. * 1)节点本身
  271. * 2)对偶节点
  272. */
  273. if (m != null) {
  274. return m;
  275. }
  276. // 如果是超时模式
  277. if (timed) {
  278. // 计算剩余时间
  279. nanos = deadline - System.nanoTime();
  280. if (nanos <= 0L) {
  281. // 已经超时,则尝试删除节点
  282. s.tryCancel();
  283. continue;
  284. }
  285. }
  286. // 1)尝试进行自旋
  287. if (spins > 0) {
  288. // 线程执行自旋
  289. Thread.onSpinWait();
  290. // 重新计算值
  291. spins = shouldSpin(s) ? spins - 1 : 0;
  292. }
  293. // 2)节点的驻留线程为 null
  294. else if (s.waiter == null) {
  295. // 写入自旋线程
  296. s.waiter = w; // establish waiter so can park next iter
  297. // 3)如果不是超时阻塞
  298. } else if (!timed) {
  299. // 阻塞当前线程
  300. LockSupport.park(this);
  301. // 4)超时时间 > 1000 纳秒
  302. } else if (nanos > SynchronousQueue.SPIN_FOR_TIMEOUT_THRESHOLD) {
  303. // 超时阻塞当前线程
  304. LockSupport.parkNanos(this, nanos);
  305. }
  306. }
  307. }
  308. /**
  309. * Unlinks s from the stack.
  310. */
  311. void clean(SNode s) {
  312. s.item = null; // forget item
  313. s.waiter = null; // forget thread
  314. // 读取后置节点
  315. SNode past = s.next;
  316. // 如果后置节点也被取消了
  317. if (past != null && past.isCancelled()) {
  318. // 更新终止节点
  319. past = past.next;
  320. }
  321. /**
  322. * 从头部开始遍历,删除已经取消的节点,
  323. * 1)直到发现一个未取消的节点 ||
  324. * 2)一直遍历到 past 为止
  325. */
  326. SNode p;
  327. while ((p = head) != null && p != past && p.isCancelled()) {
  328. casHead(p, p.next);
  329. }
  330. // 发现了一个未取消的节点,从未取消的节点开始又执行一次清除操作
  331. while (p != null && p != past) {
  332. // 读取后置节点
  333. final SNode n = p.next;
  334. // 后置节点已经取消
  335. if (n != null && n.isCancelled()) {
  336. // 则将其踢除
  337. p.casNext(n, n.next);
  338. } else {
  339. // 处理下一个节点
  340. p = n;
  341. }
  342. }
  343. }
  344. }
  • 如果队列头部是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
  1. /**
  2. * 如果栈顶是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
  3. */
  4. @Override
  5. public boolean offer(E e) {
  6. if (e == null) {
  7. throw new NullPointerException();
  8. }
  9. return transferer.transfer(e, true, 0) != null;
  10. }
  • 尝试在指定的超时时间内将目标元素 e 传递给队列头部的一个 take 操作,传递成功则返回 true,否则返回 false。
  1. /**
  2. * 尝试在指定的超时时间内将目标元素 e 传递给一个 take 操作,
  3. * 传递成功则返回 true,否则返回 false
  4. */
  5. @Override
  6. public boolean offer(E e, long timeout, TimeUnit unit)
  7. throws InterruptedException {
  8. if (e == null) {
  9. throw new NullPointerException();
  10. }
  11. if (transferer.transfer(e, true, unit.toNanos(timeout)) != null) {
  12. return true;
  13. }
  14. // 线程未被中断,则返回 false;否则抛出 InterruptedException 异常
  15. if (!Thread.interrupted()) {
  16. return false;
  17. }
  18. throw new InterruptedException();
  19. }
  • 移除并获取队列头元素,如果无可用元素,则阻塞等待
  1. /**
  2. * 移除并获取栈顶元素,如果无可用元素,则阻塞等待
  3. */
  4. @Override
  5. public E take() throws InterruptedException {
  6. final E e = transferer.transfer(null, false, 0);
  7. if (e != null) {
  8. return e;
  9. }
  10. // 返回元素为 null 表示线程被中断,则清除中断标识并抛出 InterruptedException 异常。
  11. Thread.interrupted();
  12. throw new InterruptedException();
  13. }
  • 如果队列头节点为一个数据节点,则尝试移除并返回队列头部的数据元素,否则返回 null
  1. /**
  2. * 尝试移除并返回栈顶的数据元素,如果栈顶节点为一个数据节点,否则返回 null
  3. */
  4. @Override
  5. public E poll() {
  6. return transferer.transfer(null, true, 0);
  7. }
  • 尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据,超时则返回 null。
  1. /**
  2. * 尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据。
  3. * 超时则返回 null。
  4. */
  5. @Override
  6. public E poll(long timeout, TimeUnit unit) throws InterruptedException {
  7. final E e = transferer.transfer(null, true, unit.toNanos(timeout));
  8. if (e != null || !Thread.interrupted()) {
  9. return e;
  10. }
  11. throw new InterruptedException();
  12. }

SynchronousQueue 源码分析的更多相关文章

  1. 死磕 java集合之SynchronousQueue源码分析

    问题 (1)SynchronousQueue的实现方式? (2)SynchronousQueue真的是无缓冲的吗? (3)SynchronousQueue在高并发情景下会有什么问题? 简介 Synch ...

  2. 并发编程(十)—— Java 并发队列 BlockingQueue 实现之 SynchronousQueue源码分析

    BlockingQueue 实现之 SynchronousQueue SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除 ...

  3. 【Java并发编程】17、SynchronousQueue源码分析

    SynchronousQueue是一种特殊的阻塞队列,不同于LinkedBlockingQueue.ArrayBlockingQueue和PriorityBlockingQueue,其内部没有任何容量 ...

  4. 【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

  5. JUC源码分析-集合篇(九)SynchronousQueue

    JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...

  6. lesson2:java阻塞队列的demo及源码分析

    本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...

  7. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  8. SOFA 源码分析 — 自定义线程池原理

    前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...

  9. 死磕 java集合之LinkedTransferQueue源码分析

    问题 (1)LinkedTransferQueue是什么东东? (2)LinkedTransferQueue是怎么实现阻塞队列的? (3)LinkedTransferQueue是怎么控制并发安全的? ...

随机推荐

  1. linux下安装phpunit

    安装pear 的命令如下: $ wget http://pear.php.net/go-pear.phar $ php go-pear.phar 如果报出PHP Warning:  file_exis ...

  2. while与格式化的练习

    练习 判断下列逻辑语句的结果,一定要自己先分析 1)1 > 1 or 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6 2)n ...

  3. CSRF Failed: CSRF token missing or incorrect

    Django设置本身没有关闭CSRF Django设置已经关闭CSRF,可能是由于两个项目都使用同一个端口,调试的时候就会出现Cookie里面csrftoken重用的问题,清理Cookie就好

  4. Datatable 中的数据查询(查询不重复记录)

    http://blog.csdn.net/lovexiaoxiao/article/details/3734932 //在sql中我们使用distinct查询不重复记录    //然而我在项目中表关系 ...

  5. http协议中常见的状态码以及请求方式,http协议的组成

    请求状态码: 2xxx:表示请求成功,例如200. 3xxx:表示请求被重定向,表示完成请求,需要进一步操作,例如 302. 4xxx:表示请求错误,例如:404,资源没有找到. 5xxx:表示服务器 ...

  6. PHP至Document类操作 xml 文件

    今天将项目上传到服务器后,打开项目发现报错 Error:undefined function appendChild()......, 根据提示查看源代码,发现 new Document()-> ...

  7. 计算视图相对坐标时convertPoint:toView: ,UIApplication sharedApplication - keyWindow is nil?

    UIWindow *window = [UIApplication sharedApplication].keyWindow; window 为nil的原因:在指定rootViewController ...

  8. GIT服务器项目部署和自动同步

    1.1.初始化Git仓库首先我们选定一个目录作为Git仓库,假定是/home/data/share/share.git,在/home/data/目录下输入命令: $ cd /home/data/ $ ...

  9. ifconfig-push

    ifconfig-push中的每一对IP地址表示虚拟客户端和服务器的IP端点.它们必须从连续的/30子网网段中获取(这里是/30表示xxx.xxx.xxx.xxx/30,即子网掩码位数为30),以便于 ...

  10. [效率神技]Intellij 的快捷键和效率技巧|系列一|常用快捷键

    Intellij 是个功能强大的IDE,这里只讲window下社区版的Intellij. 1. 常用快捷: Alt+回车 导入包,自动修正Ctrl+N   查找类Ctrl+Shift+N 查找文件Ct ...