SynchronousQueue------TransferStack源码分析
s,e在线程栈里面,TransferStack在堆里面,方法只是线程的执行逻辑。线程过来调用transfer方法,线程在堆里面创建一个节点,加到Stack里面去,然后这个线程归属节点的waiter,阻塞(方法局部变量保留)。配对的线程过来,在堆里创建一个节点加入stack,
配对后移除2个节点,正在配对时候,有线程带着局部变量e入队或者来交易,什么都不做只是帮助匹配(同时只能一个节点在配对),帮助配对完成之后,该入队就入队该交易就交易。
package com.itmayiedu;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
import java.util.Spliterator;
import java.util.Spliterators; public class SynchronousStack<E> {
abstract static class Transferer<E> {
abstract E transfer(E e, boolean timed, long nanos);
}
static final int NCPUS = Runtime.getRuntime().availableProcessors();
static final int maxTimedSpins = (NCPUS < ) ? : ;
static final int maxUntimedSpins = maxTimedSpins * ;
static final long spinForTimeoutThreshold = 1000L; static final class TransferStack<E> extends Transferer<E> {
static final int REQUEST = ;//消费者
static final int DATA = ;//生产者
static final int FULFILLING = ;//表示正在进行交易的节点。
static boolean isFulfilling(int m) { return (m & FULFILLING) != ; }//FULFILLING返回true,是否是正在进行交易的生产者或者消费者。
static final class SNode {
volatile SNode next;
volatile SNode match; // 相匹配的节点
volatile Thread waiter; // 等待的线程
//item域和mode域不需要使用volatile修饰,因为它们在volatile/atomic操作之前写,之后读
Object item; // item 域
int mode;// REQUEST,DATA,FULFILLING
SNode(Object item) {
this.item = item;
}
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
boolean tryMatch(SNode s) {//匹配成功,则unpark等待线程
if (match == null &&//设置本结点的匹配为s节点
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;
}
void tryCancel() {//取消这个节点,match从原来的null变为this
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
boolean isCancelled() {
return match == this;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static {
try {
UNSAFE = getUnsafe();//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);
}
}
}
volatile SNode head;//栈的头结点
boolean casHead(SNode h, SNode nh) {
return h == head &&
UNSAFE.compareAndSwapObject(this, headOffset, h, nh);//改变头节点需要cas只有一个线程成功
}
static SNode snode(SNode s, Object e, SNode next, int mode) {//入账,新进来节点下一个节点是head节点。
if (s == null) s = new SNode(e);
s.mode = mode;
s.next = next;
return s;
} E transfer(E e, boolean timed, long nanos) {
SNode s = null;
int mode = (e == null) ? REQUEST : DATA;//消费者是0生产者是1
for (;;) {
SNode h = head;//刚开始头节点为null,第一个进来的节点就是头节点。
//入队2步:构建新节点新节点.next=原来头节点,原来头节点变为新节点。
if (h == null || h.mode == mode) { // 栈为空或者当前节点模式与头节点模式一样,将节点压入栈内,等待匹配
if (timed && nanos <= ) { // 新进来的入队节点已经超时,还要看一下头节点是否取消了?只有一个头节点,看下头节点。
if (h != null && h.isCancelled())// 节点被取消了,向前推进
casHead(h, h.next);
else
return null;// 头节点没有被取消,但是这个节点已经超时,什么都不做,直接返回
} else if (casHead(h, s = snode(s, e, h, mode))) {//线程栈里面构建节点s,头节点指向最新进来的节点s,s.next=原来头节点
SNode m = awaitFulfill(s, timed, nanos);// 等待 匹配,线程阻塞时候局部变量保留,唤醒时候再次使用不变。
if (m == s) { // 返回match到的节点m == s节点自己, 表示该节点被取消了或者超时、中断了
clean(s);
return null;
}
// 先唤醒在移除节点,唤醒之后这里执行,唤醒这里执行时候有可能还没有移除,这里就帮助移除。唤醒的是第一个生产节点s=62,此时head是交易节点81, h.next == s
if ((h = head) != null && h.next == s)
casHead(h, s.next);// 将s.next节点设置为head,相当于取消节点h、s,帮助移除。
return (E) ((mode == REQUEST) ? m.item : s.item);
}
// 取节点,头节点不是正在取节点的节点,头节点没有配对。
//取节点:线程过来调用transfer方法,线程在堆里面创建一个节点改变模式为FULFILLING,加到Stack里面去,然后配对,移除2个节点,返回值。
} else if (!isFulfilling(h.mode)) {
if (h.isCancelled()) // 取节点看头节点有没有取消
casHead(h, h.next); // pop and retry
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {//消费者s进来也要入队成为头节点,将这个节点s的模式变为FULFILLING,s是交易节点,
for (;;) { // 比while效率高
SNode m = s.next; //s是交易节点81,m是被匹配的节点62
if (m == null) { // m == null,其他帮助配对的线程移除了
casHead(s, null); // 将s弹出
s = null; // 将s置空,下轮循环的时候还会新建,帮助GC
break; // 退出该循环,继续主循环
}
SNode mn = m.next;
if (m.tryMatch(s)) {//s节点和m节点匹配,配对时候唤醒节点
casHead(s, mn); // 匹配成功,将s 、 m弹出,完成交易之后将两个节点一起弹出,并且返回交易的数据。
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
s.casNext(m, mn); // 如果没有匹配成功,m有可能取消了,那么就需要把m从栈中移除。s继续跟mn做交易 没有人指向m就会被回收。
}
}
//如果栈顶已经存在一个模式为FULFILLING的节点,说明栈顶的节点正在进行匹配,那么就帮助这个栈顶节点快速完成交易,然后继续交易。
} else {
SNode m = h.next; // h=81,m=62,h是交易节点,m是被配对节点,
if (m == null) // m == null ,执行到这一行时候,配对已经被别的帮助线程或者交易节点线程自己执行完了,
casHead(h, null);
else {
SNode mn = m.next;//跟取节点差不多mn=56
if (m.tryMatch(h)) //62和81配对
casHead(h, mn); //帮助移除。h和m
else
h.casNext(m, mn);//帮助匹配失败,h的下一个节点变为下一个的下一个节点。 没有人指向m就会被回收。
}
}
}
}
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
int spins = (shouldSpin(s) ?//第一个节点要自旋
(timed ? maxTimedSpins : maxUntimedSpins) : );
for (;;) {
if (w.isInterrupted())
s.tryCancel();
SNode m = s.match;
if (m != null)
return m;//返回这个节点match到的节点
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
if (spins > )
spins = shouldSpin(s) ? (spins-) : ;
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) {//如果当前节点在栈顶,或者正在请求交易,那么就应该自旋。
//因为很可能立刻就会有新的线程到来,那么就会立刻进行交易而不需要进行阻塞,然后被唤醒,这是需要过程的,所以这样的自旋等待是值得的。
SNode h = head;
boolean b = isFulfilling(h.mode);
return (h == s || h == null || isFulfilling(h.mode));
}
void clean(SNode s) {//clean()方法就是将head节点到S节点之间所有已经取消的节点全部移出。
s.item = null; // forget item
s.waiter = null; // forget thread
SNode past = s.next;//s=39,past=38
//这个方法首先找到接下来的第一个不为null并且没有被取消交易的节点past,然后设置新的head节点,
if (past != null && past.isCancelled())
past = past.next;
SNode p;
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);//p是从头节点开始第一个不移除的节点
while (p != null && p != past) {//p到past之间的都要移除
SNode n = p.next;
if (n != null && n.isCancelled())
p.casNext(n, n.next);//移除节点n
else
p = n;//修改p指针
}
}
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
static {
try {
UNSAFE = getUnsafe();//sun.misc.Unsafe.getUnsafe();
Class<?> k = TransferStack.class;
headOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("head"));
} catch (Exception e) {
throw new Error(e);
}
}
}
private transient volatile Transferer<E> transferer;
public SynchronousStack() {
this(false);
}
public SynchronousStack(boolean fair) {
transferer = fair ? new TransferStack<E>() : new TransferStack<E>();
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, ) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
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;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
} public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, ) != null;
} public E take() throws InterruptedException {
E e = transferer.transfer(null, false, );
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
} public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = transferer.transfer(null, true, unit.toNanos(timeout));
if (e != null || !Thread.interrupted())
return e;
throw new InterruptedException();
} public E poll() {
return transferer.transfer(null, true, );
} private static sun.misc.Unsafe getUnsafe() {
try {
return sun.misc.Unsafe.getUnsafe();
} catch (SecurityException tryReflectionInstead) {}
try {
return java.security.AccessController.doPrivileged
(new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
public sun.misc.Unsafe run() throws Exception {
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
for (java.lang.reflect.Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x))
return k.cast(x);
}
throw new NoSuchFieldError("the Unsafe");
}});
} catch (java.security.PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics",
e.getCause());
}
}
}
SynchronousQueue------TransferStack源码分析的更多相关文章
- 【JUC】JDK1.8源码分析之SynchronousQueue(九)
一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...
- 死磕 java集合之SynchronousQueue源码分析
问题 (1)SynchronousQueue的实现方式? (2)SynchronousQueue真的是无缓冲的吗? (3)SynchronousQueue在高并发情景下会有什么问题? 简介 Synch ...
- JUC源码分析-集合篇(九)SynchronousQueue
JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...
- 细说并发5:Java 阻塞队列源码分析(下)
上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...
- 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是怎么控制并发安全的? ...
- JDK源码分析(11)之 BlockingQueue 相关
本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ...
- 并发编程之 Exchanger 源码分析
前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...
随机推荐
- vue_ajax 请求
yarn add vue-resource axios npm install --save axios pubsub-js // import VueResource from "vue- ...
- Eclipse导出自己的项目(仅供自己保留方式war包)
War: Jar包:
- shell - shift
Shell编程中Shift的用法 位置参数可以用shift命令左移.比如 shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1.$2.$3丢弃,$0不移动.不带参数的shi ...
- SyntaxError: invalid character in identifier(Python)
在写博客时直接将博客上的代码复制运行后发现错误SyntaxError: invalid character in identifier,我以为是l(小L)写成了1,改了还是不行. 上网查了下,发现原来 ...
- 必备Linux命令和C语言基础
每一个学习嵌入式单片机的伙伴我相信对于这两个都不陌生,这毕竟是嵌入式单片机的生存之道 所有基础还是要打牢的 有句老话说的好基础不牢地动山摇 下面看下系统的资料吧 希望能对大家有所帮 ...
- Kali 开启 SSH 服务方法
尝试了开启kali的ssh,方法如下: 1.修改sshd_config文件.命令:vim /etc/ssh/sshd_config 2.将#PasswordAuthentication no的注释去掉 ...
- 好的UI管理后台
1,https://www.v2ex.com/t/513539 - https://github.com/a54552239/projectManage
- box-sizing:border-boxing的使用
<div class="box"></div> .box { margin-top: 200px; margin-left: 200px; backgrou ...
- DCDC参数测量及方法
此文章目的为补充知识,防止遗忘,记录DCDC相关的. 1.拿到一颗DCDC芯片应该测试哪些参数:纹波.电源效率和动态响应. 1)纹波测量方法:示波器偶合方式选择AC:示波器探头的接地也不能用鳄鱼夹,这 ...
- vi光标移动
1.上下左右移动 k :上移一行 j :下移一行 h :左移一行 l :右移一行 2.移到当前屏幕的首.中.尾部 H :移到当前屏幕的首部 M :移到当前屏幕的中部 L :移到当前屏幕的尾部 ...