Exchanger的大解剖:
这东西是一个数据交换器 线程在这个交换器里面进行数据的交换 大致如图
线程1的数据在交换器(Exchanger)里面等待线程2的数据到来进行数据的交换
交换的条件是成对的产生 同时可以有多对线程进行交换
代码演示
准备线程A
//线程A
class ExchangerA extends Thread{
//保证同一个交换器
private Exchanger<String> exchanger =null;
public ExchangerA (Exchanger exchanger){
this.exchanger=exchanger;
}
public void run(){
System.out.println("这里是A线程"+Thread.currentThread().getName());
try {
Thread.sleep(3000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A的数据这边和B的数据准备进行交换");
try {
String s = exchanger.exchange("这是A的数据为11111");
System.out.println("这是在A现场里交换回来B的信息为"+s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A和B交换完成继续干活");
}
}
准备线程B
//线程B
class ExchangerB extends Thread{
//保证同一个交换器
private Exchanger<String> exchanger =null;
public ExchangerB (Exchanger exchanger) {
this.exchanger = exchanger;
}
public void run(){
System.out.println("这里是B线程"+Thread.currentThread().getName());
try {
Thread.sleep(3000l);
System.out.println("B准备完成等待交换数据");
String ss = exchanger.exchange("这是B的数据为222");
System.out.println("这是在B线程里A的数据为"+ss);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A和B数据交换完成进行接下来的工作");
}
}
启动测试
public class ExchangerCS {
public static void main(String[] args) {
Exchanger<String> stringExchanger = new Exchanger<>();
new ExchangerA(stringExchanger).start();
new ExchangerB(stringExchanger).start();
}
}
Exchanger中核心方法的源码分析
当我们在新建一个Exchanger的时候会在泛型声明类型 而这个泛型不能Exchanger之间交换数据的类型 它是我们给一个泛型给Exchanger 然后它把我们的类型统一用node节点来进行接收 交换也是node节点进行交换
item的位置是线程A数据放的地方
match是未来进行和A线程交换的数据位置
parked是先到的线程在这里进行一个等待
Exchanger的核心方法
exchanger.exchange
大致意思是是 当第一线程带数据进来了 会先进行判断是单槽位交换还是多槽位的交换
单槽位走 slotExchange
多槽位走arenaExchange
这里走单槽位方法
private final Object slotExchange(Object item, boolean timed, long ns) {
//线程A先进来拿到当前带来的node1数据
Node p = participant.get();
//获取到当前的线程A
Thread t = Thread.currentThread();
//判断线程A状态是否中断
if (t.isInterrupted()) // preserve interrupt status so caller can recheck
return null;
for (Node q;;) {
//判断单槽位节点的值是不是为空
if ((q = slot) != null) {
if (U.compareAndSwapObject(this, SLOT, q, null)) {
Object v = q.item;
//线程B的值就放到线程A的match里面
q.match = item;
Thread w = q.parked;
if (w != null)
//唤醒线程A
U.unpark(w);
return v;
}
// create arena on contention, but continue until slot null
if (NCPU > 1 && bound == 0 &&
U.compareAndSwapInt(this, BOUND, 0, SEQ))
arena = new Node[(FULL + 2) << ASHIFT];
}
else if (arena != null)
return null; // caller must reroute to arenaExchange
else {
//代码逻辑先到这里把线程A带来的数据值给了node1里面的item里面
p.item = item;
//这里让slot有了值 slot槽点就不为null
if (U.compareAndSwapObject(this, SLOT, null, p))
break;
p.item = null;
}
}
// await release
//下面是拿到当前节点的hash值去计算一些条件
int h = p.hash;
long end = timed ? System.nanoTime() + ns : 0L;
int spins = (NCPU > 1) ? SPINS : 1;
Object v;
//优化的操作 不用太了解
while ((v = p.match) == null) {
if (spins > 0) {
h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
if (h == 0)
h = SPINS | (int)t.getId();
else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield();
}
//优化的操作 配对的线程到了还没有准备好 不用太了解
else if (slot != p)
spins = SPINS;
//
else if (!t.isInterrupted() && arena == null &&
(!timed || (ns = end - System.nanoTime()) > 0L)) {
U.putObject(t, BLOCKER, this);
//给这个node节点的park记下当前的线程
p.parked = t;
if (slot == p)
U.park(false, ns);
p.parked = null;
U.putObject(t, BLOCKER, null);
}
else if (U.compareAndSwapObject(this, SLOT, p, null)) {
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
break;
}
}
U.putOrderedObject(p, MATCH, null);
p.item = null;
p.hash = h;
return v;
}
简单的图解一下
最好还是在看看源码一步步的点进去看比较有用
P:线程进入Exchanger带来的node数据
slot:单槽位节点(一般第一个线程进入Exchanger这个值都为inull)
q:q类似于slot 会把slot进行替代 并把slot赋值为null
item:存线程带来的数据
Match:存交换来的数据
parked:存当前线程
源码中的一些条件判断 第一次线程进入不能生成 线程B进入会生成 最重要是自己debug看看大致流程
欢迎进行学习交流,不足之处请指出,喜欢麻烦点赞+收藏,谢谢各位大佬了