是否有办法确保从将来创建的单个Mono
的所有转换步骤都在订阅和阻止的线程上执行?
例如,以下代码
public static void main(String[] args) {
var future = new CompletableFuture<String>();
var res = Mono.fromFuture(future).map(val -> {
System.out.println("Thread: " + Thread.currentThread().getName());
return val + "1";
});
new Thread(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
}
future.complete("completed");
}, "completer").start();
res.block();
}
打印Thread: completer
,因为将来是通过“ completer”线程完成的。我正在尝试确定是否有办法始终打印Thread: main
。
答案 0 :(得分:2)
不。当import types
import copyreg
import multiprocessing
def my_reduce(obj):
return (obj.__func__.__get__, (obj.__self__,))
copyreg.pickle(types.MethodType, my_reduce)
multiprocessing.reduction.register(types.MethodType, my_reduce)
线程通过main
被阻塞时,该线程专门在等待流的.block()
,onNext
或onComplete
信号(毕竟,上游操作员已执行)。在调用上游操作符以执行操作符之前,它不以某种方式重新获得控制权。
您可以做的最接近的事情是确保:
onError
上执行订阅(通过Scheduler
)并且.subscribeOn
上(通过Scheduler
)。例如:
.publishOn
但是,请注意以下几点:
Scheduler scheduler = Schedulers.parallel();
var res = Mono.fromFuture(future)
.doFirst(() -> { // Note: doFirst added in 3.2.10.RELEASE
// prints a thread in the parallel Scheduler (specified by subscribeOn below)
System.out.println("Subscribe Thread: " + Thread.currentThread().getName());
})
// specifies the Scheduler on which the the completion value
// from above is published for downstream operators
.publishOn(scheduler)
.map(val -> {
// prints a thread in the parallel Scheduler (specified by publishOn above)
System.out.println("Operator Thread: " + Thread.currentThread().getName());
return val + "1";
})
// specifies the Scheduler on which upstream operators are subscribed
.subscribeOn(scheduler);
中的线程上,不是被阻塞的Scheduler
线程中。main
,而不是Scheduler
中使用相同的Thread
。从理论上讲,您可以使用单线程调度程序(例如Scheduler
)强制使用相同的Thread
Schedulers.newParallel("single-threaded", 1)
不会强制 所有运算符在该.publishOn
上进行操作。它只会影响下游运算符,直到下一个Scheduler
为止,或者直到下一个可能使用不同的.publishOn
的异步运算符(例如.flatMap
)为止。答案 1 :(得分:0)
作为一种非常未经优化的概念证明,可以通过以下方式实现:
让我们创建一个执行器,该执行器能够以受控方式“按需”执行任务。
private static class SelfEventLoopExecutor implements Executor {
private final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
@Override
public void execute(Runnable command) {
boolean added = queue.add(command);
assert added;
}
public void drainQueue() {
Runnable r;
while ((r = queue.poll()) != null) {
r.run();
}
}
}
接下来,创建一个订阅者,该订阅者可以使用执行程序在等待结果的同时执行任务,而不是完全阻塞线程。
public static class LazyBlockingSubscriber<T> implements Subscriber<T> {
private final SelfEventLoopExecutor selfExec;
private volatile boolean completed = false;
private volatile T value;
private volatile Throwable ex;
public LazyBlockingSubscriber(SelfEventLoopExecutor selfExec) {
this.selfExec = selfExec;
}
@Override
public void onSubscribe(Subscription s) {
s.request(1);
}
@Override
public void onNext(T t) {
value = t;
completed = true;
}
@Override
public void onError(Throwable t) {
ex = t;
completed = true;
}
@Override
public void onComplete() {
completed = true;
}
public T block() throws Throwable {
while (!completed) {
selfExec.drainQueue();
}
if (ex != null) {
throw ex;
}
return value;
}
}
现在,我们可以通过以下方式修改代码
public static void main(String[] args) throws Throwable {
var future = new CompletableFuture<String>();
var selfExec = new SelfEventLoopExecutor(); // our new executor
var res = Mono.fromFuture(future)
.publishOn(Schedulers.fromExecutor(selfExec)) // schedule on the new executor
.map(val -> {
System.out.println("Thread: " + Thread.currentThread().getName());
return val + "1";
});
new Thread(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
}
future.complete("completed");
}, "completer").start();
var subs = new LazyBlockingSubscriber<String>(selfExec); // lazy subscribe
res.subscribeWith(subs);
subs.block(); // spin wait
}
结果,代码将打印Thread: main
。