作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
学习必须往深处挖,挖的越深,基础越扎实!
阶段1、深入多线程
阶段2、深入多线程设计模式
阶段3、深入juc源码解析
码哥源码部分
码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】
码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】
码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】
码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】
打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】
一、LinkedTransferQueue简介
LinkedTransferQueue
是在JDK1.7时,J.U.C包新增的一种比较特殊的阻塞队列,它除了具备阻塞队列的常用功能外,还有一个比较特殊的transfer
方法。
我们知道,在普通阻塞队列中,当队列为空时,消费者线程(调用 take 或 poll 方法的线程)一般会阻塞等待生产者线程往队列中存入元素。而 LinkedTransferQueue 的 transfer 方法则比较特殊:
- 当有消费者线程阻塞等待时,调用transfer方法的生产者线程不会将元素存入队列,而是直接将元素传递给消费者;
- 如果调用transfer方法的生产者线程发现没有正在等待的消费者线程,则会将元素入队,然后会阻塞等待,直到有一个消费者线程来获取该元素。
TransferQueue接口
可以看到,LinkedTransferQueue实现了一个名为TransferQueue
的接口,TransferQueue也是JDK1.7时J.U.C包新增的接口,正是该接口提供了上述的transfer方法:
除了transfer方法外,TransferQueue还提供了两个变种方法:tryTransfer(E e)
、tryTransfer(E e, long timeout, TimeUnit unit)
。
tryTransfer(E e)
当生产者线程调用tryTransfer方法时,如果没有消费者等待接收元素,则会立即返回false。该方法和transfer方法的区别就是tryTransfer方法无论消费者是否接收,方法立即返回,而transfer方法必须等到消费者消费后才返回。
tryTransfer(E e, long timeout, TimeUnit unit)
tryTransfer(E e,long timeout,TimeUnit unit)方法则是加上了限时等待功能,如果没有消费者消费该元素,则等待指定的时间再返回;如果超时还没消费元素,则返回false,如果在超时时间内消费了元素,则返回true。
TransferQueue接口定义:
LinkedTransferQueue的特点简要概括如下:
- LinkedTransferQueue是一种无界阻塞队列,底层基于单链表实现;
- LinkedTransferQueue中的结点有两种类型:数据结点、请求结点;
- LinkedTransferQueue基于无锁算法实现。
二、LinkedTransferQueue原理
2.1 内部结构
LinkedTransferQueue提供了两种构造器,也没有参数设置队列初始容量,所以是一种 无界队列 :
/**
* 队列结点定义.
*/
static final class Node {
final boolean isData; // true: 数据结点; false: 请求结点
volatile Object item; // 结点值
volatile Node next; // 后驱结点指针
volatile Thread waiter; // 等待线程
// 设置当前结点的后驱结点为val
final boolean casNext(Node cmp, Node val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// 设置当前结点的值为val
final boolean casItem(Object cmp, Object val) {
// assert cmp == null || cmp.getClass() != Node.class;
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
Node(Object item, boolean isData) {
UNSAFE.putObject(this, itemOffset, item); // relaxed write
this.isData = isData;
}
// 设置当前结点的后驱结点为自身
final void forgetNext() {
UNSAFE.putObject(this, nextOffset, this);
}
/**
* 设置当前结点的值为自身.
* 设置当前结点的等待线程为null.
*/
final void forgetContents() {
UNSAFE.putObject(this, itemOffset, this);
UNSAFE.putObject(this, waiterOffset, null);
}
/**
* 判断当前结点是否匹配成功.
* Node.item == this || (Node.isData == true && Node.item == null)
*/
final boolean isMatched() {
Object x = item;
return (x == this) || ((x == null) == isData);
}
/**
* 判断是否为未匹配的请求结点.
* Node.isData == false && Node.item == null
*/
final boolean isUnmatchedRequest() {
return !isData && item == null;
}
/**
* 当该结点(havaData)是未匹配结点, 且与当前的结点类型不同时, 返回true.
*/
final boolean cannotPrecede(boolean haveData) {
boolean d = isData;
Object x;
return d != haveData && (x = item) != this && (x != null) == d;
}
/**
* 尝试匹配数据结点.
*/
final boolean tryMatchData() {
// assert isData; 当前结点必须为数据结点
Object x = item;
if (x != null && x != this && casItem(x, null)) {
LockSupport.unpark(waiter); // 唤醒等待线程
return true;
}
return false;
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
private static final long waiterOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = Node.class;
itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
waiterOffset = UNSA