SynchronousQueue 源码分析
SynchronousQueue
SynchronousQueue 能解决什么问题?什么时候使用 SynchronousQueue?
1)SynchronousQueue 没有任何内部容量。
2)SynchronousQueue 中每一个插入操作必须等待一个与之配对的删除操作,反之亦然。
3)SynchronousQueue 非常适合于线程之间交换信息,均衡生产者与消费者的处理速率。
4)SynchronousQueue 的性能和吞吐量高于 LinkedBlockingQueue 和 ArrayBlockingQueue。
如何使用 SynchronousQueue?
1)Executors.newCachedThreadPool() 使用 SynchronousQueue 作为其任务队列。
2)需要均衡生产和消费速率时,可以使用 SynchronousQueue。
使用 SynchronousQueue 有什么风险?
1)生产速率持续高于消费速率时,会导致生产者阻塞,服务不可用。
SynchronousQueue 核心操作的实现原理?
- 创建实例
/**
* Shared internal API for dual stacks and queues.
*/
abstract static class Transferer<E> {
/**
* 执行一个 put 或 take 操作
*
* @param e 1)值为 null,则表示这是一个 take 操作
* 2)值不为 null,则表示这是一个 put 操作
* @param timed 操作的超时时间
* @param nanos 以纳秒为单位
* @return 1)值为 null 表示超时或线程被中断
* 2)值不为 null,表示插入或读取的目标元素
*/
abstract E transfer(E e, boolean timed, long nanos);
}
/**
* 超时等待前的自旋次数
*/
static final int MAX_TIMED_SPINS =
Runtime.getRuntime().availableProcessors() < 2 ? 0 : 32;
/**
* 阻塞等待前的自旋次数
*/
static final int MAX_UNTIMED_SPINS = SynchronousQueue.MAX_TIMED_SPINS * 16;
/**
* 超时时间小于 1000 纳秒时,自旋比阻塞等待效率高
*/
static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
/**
* 转移接口
*/
private transient volatile Transferer<E> transferer;
/**
* 创建一个非公平的同步队列
*/
public SynchronousQueue() {
this(false);
}
/**
* fair==true,创建一个公平的同步队列,最先等待的最先释放
* fair==false,创建一个非公平的同步队列
*/
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<>() : new TransferStack<>();
}
- 将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
/**
* 将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
*/
@Override
public void put(E e) throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
/** Dual stack */
static final class TransferStack<E> extends Transferer<E> {
/** 节点代表一个未完成的消费者 */
static final int REQUEST = 0;
/** 节点代表一个未完成的生产者 */
static final int DATA = 1;
/** 节点正在完成一个匹配的操作 */
static final int FULFILLING = 2;
/** Node class for TransferStacks. */
static final class SNode {
// 堆栈的下一个节点
volatile SNode next;
// 与当前节点匹配的对偶节点
volatile SNode match;
// 当前节点驻留的线程
volatile Thread waiter;
// 如果是数据节点则非空,如果是请求节点则为 null
Object item;
// 当前节点的状态
int mode;
SNode(Object item) {
this.item = item;
}
/**
* 参数原子更新 next 节点
*/
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
SNode.SNEXT.compareAndSet(this, cmp, val);
}
/**
* 尝试将目标节点匹配给当前节点,如果成功则唤醒驻留在当前节点的线程
*/
boolean tryMatch(SNode s) {
// 尝试设置匹配节点为 s
if (match == null &&
SNode.SMATCH.compareAndSet(this, null, s)) {
// 读取驻留线程
final Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
// 唤醒驻留线程
LockSupport.unpark(w);
}
// 匹配成功返回 true
return true;
}
return match == s;
}
/**
* 将匹配节点设置为 this 表示节点被删除
*/
void tryCancel() {
SNode.SMATCH.compareAndSet(this, null, this);
}
/**
* 当前节点已经被取消
* created by ZXD at 8 Dec 2018 T 11:11:25
*/
boolean isCancelled() {
return match == this;
}
// VarHandle mechanics
private static final VarHandle SMATCH;
private static final VarHandle SNEXT;
static {
try {
final MethodHandles.Lookup l = MethodHandles.lookup();
SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
} catch (final ReflectiveOperationException e) {
throw new Error(e);
}
}
}
/** 堆栈顶部元素 */
volatile SNode head;
/**
* Puts or takes an item.
*/
@Override
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {
/**
* 1)如果堆栈为空或包含相同类型的节点,则新建节点并入栈,阻塞等待。
* 2)如果堆栈中包含对偶节点,则将一个对偶节点入栈,
* 并同时弹出新增节点及与其匹配的节点。
* 3)如果堆栈顶部已经持有对偶节点,则帮助它们弹出堆栈。
*/
SNode s = null; // constructed/reused as needed
// 计算节点模式
final int mode = e == null ? TransferStack.REQUEST : TransferStack.DATA;
for (;;) {
// 读取栈顶节点
SNode h = head;
// 1)堆栈为空或栈顶元素和新增节点模式一致
if (h == null || h.mode == mode) { // empty or same-mode
// 1-1)如果设置了超时时间,并且已经超时
if (timed && nanos <= 0L) { // can't wait
// 头节点不为 null && 头节点已经被取消
if (h != null && h.isCancelled()) {
// 尝试原子更新栈顶节点
casHead(h, h.next); // pop cancelled node
} else {
// 删除栈顶后返回 null
return null;
}
// 1-2)尝试将新节点入栈
} else if (casHead(h, s = TransferStack.snode(s, e, h, mode))) {
// 尝试在指定的超时时间内等待对偶节点
final 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
}
/**
* 1)新值的是请求节点,则返回对偶节点的数据值
* 2)新值的是数据节点,则返回其持有的数据
*/
return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
}
// 2)头节点不是 fulFill 节点
} else if (!TransferStack.isFulfilling(h.mode)) { // try to fulfill
// 1)头节点已经被取消
if (h.isCancelled()) {
// 则重新写入头结点
casHead(h, h.next); // pop and retry
// 2)尝试写入一个 fulFill 节点到栈顶
} else if (casHead(h, s=TransferStack.snode(s, e, h, TransferStack.FULFILLING|mode))) {
for (;;) { // loop until matched or waiters disappear
// 读取匹配节点
final 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
}
// 读取匹配节点的后置节点
final SNode mn = m.next;
// 如果仍处于匹配模式
if (m.tryMatch(s)) {
// 同时弹出这两个节点
casHead(s, mn); // pop both s and m
/**
* 1)新值的是请求节点,则返回对偶节点的数据值
* 2)新值的是数据节点,则返回其持有的数据
*/
return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
}
else {
s.casNext(m, mn); // help unlink
}
}
}
// 3)头结点是一个 fulFill 节点,则帮助将它和它的对偶节点出栈
} else { // help a fulfiller
final SNode m = h.next; // m is h's match
if (m == null) {
casHead(h, null); // pop fulfilling node
} else {
final SNode mn = m.next;
if (m.tryMatch(h)) {
casHead(h, mn); // pop both h and m
}
else {
h.casNext(m, mn); // help unlink
}
}
}
}
}
/**
* 尝试原子更新头结点
* created by ZXD at 8 Dec 2018 T 10:56:47
* @param h
* @param nh
* @return
*/
boolean casHead(SNode h, SNode nh) {
return h == head &&
TransferStack.SHEAD.compareAndSet(this, h, nh);
}
/**
* 将节点加入栈顶
*/
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;
}
/**
* 线程执行自旋或阻塞等待,直到堆栈中写入一个对偶节点、线程超时、被其他线程中断
*
* @param s 等待节点
* @param timed 是否是超时模式
* @param nanos 超时纳秒
* @return 1)出现对偶节点,则返回对偶节点
* 2)线程超时或被中断,则返回 s
*/
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
/*
* When a node/thread is about to block, it sets its waiter
* field and then rechecks state at least one more time
* before actually parking, thus covering race vs
* fulfiller noticing that waiter is non-null so should be
* woken.
*
* When invoked by nodes that appear at the point of call
* to be at the head of the stack, calls to park are
* preceded by spins to avoid blocking when producers and
* consumers are arriving very close in time. This can
* happen enough to bother only on multiprocessors.
*
* The order of checks for returning out of main loop
* reflects fact that interrupts have precedence over
* normal returns, which have precedence over
* timeouts. (So, on timeout, one last check for match is
* done before giving up.) Except that calls from untimed
* SynchronousQueue.{poll/offer} don't check interrupts
* and don't wait at all, so are trapped in transfer
* method rather than calling awaitFulfill.
*/
// 计算截止时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 读取当前线程
final Thread w = Thread.currentThread();
// 计算自旋次数
int spins = shouldSpin(s)
? timed ? SynchronousQueue.MAX_TIMED_SPINS : SynchronousQueue.MAX_UNTIMED_SPINS
: 0;
for (;;) {
// 线程已经被中断
if (w.isInterrupted()) {
// 则尝试删除节点
s.tryCancel();
}
// 读取匹配节点
final SNode m = s.match;
/**
* 如果存在匹配节点,则返回它
* 1)节点本身
* 2)对偶节点
*/
if (m != null) {
return m;
}
// 如果是超时模式
if (timed) {
// 计算剩余时间
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
// 已经超时,则尝试删除节点
s.tryCancel();
continue;
}
}
// 1)尝试进行自旋
if (spins > 0) {
// 线程执行自旋
Thread.onSpinWait();
// 重新计算值
spins = shouldSpin(s) ? spins - 1 : 0;
}
// 2)节点的驻留线程为 null
else if (s.waiter == null) {
// 写入自旋线程
s.waiter = w; // establish waiter so can park next iter
// 3)如果不是超时阻塞
} else if (!timed) {
// 阻塞当前线程
LockSupport.park(this);
// 4)超时时间 > 1000 纳秒
} else if (nanos > SynchronousQueue.SPIN_FOR_TIMEOUT_THRESHOLD) {
// 超时阻塞当前线程
LockSupport.parkNanos(this, nanos);
}
}
}
/**
* Unlinks s from the stack.
*/
void clean(SNode s) {
s.item = null; // forget item
s.waiter = null; // forget thread
// 读取后置节点
SNode past = s.next;
// 如果后置节点也被取消了
if (past != null && past.isCancelled()) {
// 更新终止节点
past = past.next;
}
/**
* 从头部开始遍历,删除已经取消的节点,
* 1)直到发现一个未取消的节点 ||
* 2)一直遍历到 past 为止
*/
SNode p;
while ((p = head) != null && p != past && p.isCancelled()) {
casHead(p, p.next);
}
// 发现了一个未取消的节点,从未取消的节点开始又执行一次清除操作
while (p != null && p != past) {
// 读取后置节点
final SNode n = p.next;
// 后置节点已经取消
if (n != null && n.isCancelled()) {
// 则将其踢除
p.casNext(n, n.next);
} else {
// 处理下一个节点
p = n;
}
}
}
}
- 如果队列头部是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
/**
* 如果栈顶是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
*/
@Override
public boolean offer(E e) {
if (e == null) {
throw new NullPointerException();
}
return transferer.transfer(e, true, 0) != null;
}
- 尝试在指定的超时时间内将目标元素 e 传递给队列头部的一个 take 操作,传递成功则返回 true,否则返回 false。
/**
* 尝试在指定的超时时间内将目标元素 e 传递给一个 take 操作,
* 传递成功则返回 true,否则返回 false
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
if (transferer.transfer(e, true, unit.toNanos(timeout)) != null) {
return true;
}
// 线程未被中断,则返回 false;否则抛出 InterruptedException 异常
if (!Thread.interrupted()) {
return false;
}
throw new InterruptedException();
}
- 移除并获取队列头元素,如果无可用元素,则阻塞等待
/**
* 移除并获取栈顶元素,如果无可用元素,则阻塞等待
*/
@Override
public E take() throws InterruptedException {
final E e = transferer.transfer(null, false, 0);
if (e != null) {
return e;
}
// 返回元素为 null 表示线程被中断,则清除中断标识并抛出 InterruptedException 异常。
Thread.interrupted();
throw new InterruptedException();
}
- 如果队列头节点为一个数据节点,则尝试移除并返回队列头部的数据元素,否则返回 null
/**
* 尝试移除并返回栈顶的数据元素,如果栈顶节点为一个数据节点,否则返回 null
*/
@Override
public E poll() {
return transferer.transfer(null, true, 0);
}
- 尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据,超时则返回 null。
/**
* 尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据。
* 超时则返回 null。
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
final E e = transferer.transfer(null, true, unit.toNanos(timeout));
if (e != null || !Thread.interrupted()) {
return e;
}
throw new InterruptedException();
}
SynchronousQueue 源码分析的更多相关文章
- 死磕 java集合之SynchronousQueue源码分析
问题 (1)SynchronousQueue的实现方式? (2)SynchronousQueue真的是无缓冲的吗? (3)SynchronousQueue在高并发情景下会有什么问题? 简介 Synch ...
- 并发编程(十)—— Java 并发队列 BlockingQueue 实现之 SynchronousQueue源码分析
BlockingQueue 实现之 SynchronousQueue SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除 ...
- 【Java并发编程】17、SynchronousQueue源码分析
SynchronousQueue是一种特殊的阻塞队列,不同于LinkedBlockingQueue.ArrayBlockingQueue和PriorityBlockingQueue,其内部没有任何容量 ...
- 【JUC】JDK1.8源码分析之SynchronousQueue(九)
一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...
- JUC源码分析-集合篇(九)SynchronousQueue
JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- SOFA 源码分析 — 自定义线程池原理
前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...
- 死磕 java集合之LinkedTransferQueue源码分析
问题 (1)LinkedTransferQueue是什么东东? (2)LinkedTransferQueue是怎么实现阻塞队列的? (3)LinkedTransferQueue是怎么控制并发安全的? ...
随机推荐
- hdu1465不easy系列之中的一个(错排)
版权声明:本文为博主原创文章,未经博主同意不得转载. vasttian https://blog.csdn.net/u012860063/article/details/37512659 转载请注明出 ...
- Linux安装 jdk&maven
JDK安装 1. 下载JDK压缩包http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ...
- java中关键字final,finally,finalize的区别
-final:作为修饰符关键字,有三种用法: 1,如果一个类被声明为final,则意味着它不能被继承. 2,将变量声明为final,则表示它是一个常量,也就是保证它在使用过程中不被修改,被final修 ...
- 【学习总结】快速上手Linux玩转典型应用-第2章-linux简介
课程目录链接 快速上手Linux玩转典型应用-目录 目录 1. 什么是Linux 2. Linux能够做什么事情 3. Linux的学习方法 4. 忘掉Windows的所有东西 1. 什么是Linux ...
- 中国各个省市县的人口统计,echart展示
公司要做一个excel形式的人口统计表,我感觉应该更直观一些展示,所以就选用了echart进行展示,由于时间短所以制作的比较简单粗糙,但相应的数据还是有很大的可参考性. 刚好下载了jfinal3.5, ...
- 关于Mysql 修改密码的记录
初次安装后完毕,使用管理员身份进入cmd界面, 输入" mysql -u root -p",出现"Enter password:",直接回车输入" s ...
- JS中对象的定义及相关操作
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- pandas的settingwithWaring报警
# 0 读取数据 import pandas as pd df = pd.read_csv("beijing_tianqi_2018.csv") # 换掉温度后面的后缀 df.lo ...
- BZOJ4625 [BJOI2016]水晶 最小割
题意简述 给你一个三维的坐标系,坐标系上 \((x_i+y_i+z_i)\bmod 3 = 0\) 的点内有能量源.给定 \(n\) 个点含有能量值为 \(c_i\) 的水晶,如果一个水晶位于能量源上 ...
- Zookeeper实现哨兵机制
master选举使用场景及结构 现在很多时候我们的服务需要7*24小时工作,假如一台机器挂了,我们希望能有其它机器顶替它继续工作.此类问题现在多采用master-salve模式,也就是常说的主从模式, ...