簡(jiǎn)介
continuation是服務(wù)于虛擬線程的一個(gè)類,主要提供run及yield的功能,使語(yǔ)言可以在任意點(diǎn)保存執(zhí)行狀態(tài)并且在之后的某個(gè)點(diǎn)返回。功能的主要實(shí)現(xiàn)都是在jvm源碼中,Continuation.java 僅是流程代碼。
如需使用,需添加以下指令到啟動(dòng)到j(luò)vm option:
--add-exports java.base/jdk.internal.vm=ALL-UNNAMED
run執(zhí)行路徑:

yield執(zhí)行路徑:

1、字段方法說(shuō)明:
從結(jié)構(gòu)上來(lái)看,Continuation有兩個(gè)維度,一個(gè)是縱向的鏈表維度(存在parent及child節(jié)點(diǎn)),一個(gè)是橫向的scope維度,相同scope的屬于同一類continuation(從代碼實(shí)現(xiàn)角度來(lái)說(shuō),scope相對(duì)于對(duì)continuation打上個(gè)tag,使得用戶在需要yield時(shí)不會(huì)yield錯(cuò)了對(duì)象)
// unsafe實(shí)例
private static final Unsafe U = Unsafe.getUnsafe();
// 是否開(kāi)啟本地緩存
private static final boolean PRESERVE_EXTENT_LOCAL_CACHE;
// JavaLangAccess操作對(duì)象,主要用于對(duì) Java 核心類庫(kù)中的一些非公開(kāi)方法和字段的訪問(wèn)
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
// 實(shí)際運(yùn)行的 runnable
private final Runnable target;
/* While the native JVM code is aware that every continuation has a scope, it is, for the most part,
* oblivious to the continuation hierarchy. The only time this hierarchy is traversed in native code
* is when a hierarchy of continuations is mounted on the native stack.
*/
// scope對(duì)象,使用同一scope的Continuation可以相互之間yield
private final ContinuationScope scope;
// 父節(jié)點(diǎn)
private Continuation parent; // null for native stack
// 子節(jié)點(diǎn)
private Continuation child; // non-null when we're yielded in a child continuation
// 棧內(nèi)存空間,在java類中沒(méi)有賦值,賦值操作是在native方法中進(jìn)行
private StackChunk tail;
// 當(dāng)前Continuation是否已完成
private boolean done;
// 裝載狀態(tài)
private volatile boolean mounted = false;
// yield信息(可能是scope,也可能是yield的結(jié)果)
private Object yieldInfo;
//
private boolean preempted;
private Object[] extentLocalCache;
2、RUN方法
public final void run() {
while (true) {
// 裝載
mount();
JLA.setExtentLocalCache(extentLocalCache);
// 已完成時(shí)再調(diào)用run則拋異常
if (done)
throw new IllegalStateException("Continuation terminated");
// 獲取當(dāng)前載體線程
Thread t = currentCarrierThread();
// 當(dāng)parent和cild都yield時(shí),child先于paret run時(shí),會(huì)進(jìn)入此if
if (parent != null) {
if (parent != JLA.getContinuation(t))
throw new IllegalStateException();
} else
this.parent = JLA.getContinuation(t);
JLA.setContinuation(t, this);
try {
boolean isVirtualThread = (scope == JLA.virtualThreadContinuationScope());
// 此處判斷是否存在堆棧內(nèi)存空間,如不存在則說(shuō)明未開(kāi)始
if (!isStarted()) { // is this the first run? (at this point we know !done)
// enterSpecial -> enter -> enter0 -> target.run
enterSpecial(this, false, isVirtualThread);
} else {
assert !isEmpty();
enterSpecial(this, true, isVirtualThread);
}
} finally {
// 此處為什么需要讀寫屏障
fence();
try {
assert isEmpty() == done : "empty: " + isEmpty() + " done: " + done + " cont: "
+ Integer.toHexString(System.identityHashCode(this));
//
JLA.setContinuation(currentCarrierThread(), this.parent);
if (parent != null)
parent.child = null;
postYieldCleanup();
unmount();
if (PRESERVE_EXTENT_LOCAL_CACHE) {
extentLocalCache = JLA.extentLocalCache();
} else {
extentLocalCache = null;
}
JLA.setExtentLocalCache(null);
} catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}
}
// we're now in the parent continuation
assert yieldInfo == null || yieldInfo instanceof ContinuationScope;
// 唯一跳出循環(huán)的點(diǎn)
if (yieldInfo == null || yieldInfo == scope) {
this.parent = null;
this.yieldInfo = null;
return;
} else {
// 進(jìn)入此代碼塊必要條件是yieldInfo存在且不為當(dāng)前的scope,既yield的scope非自身的scope
parent.child = this;
// 此時(shí)調(diào)用 yield0 方法,將當(dāng)前的continuation及需要yield的scope傳遞,直到匹配到y(tǒng)ieldInfo == scope,即鏈表向上查找
parent.yield0((ContinuationScope) yieldInfo, this);
// 斷鏈
parent.child = null;
}
}
}
3、Yield方法
疑問(wèn):為什么yield方法要設(shè)計(jì)成靜態(tài)的方法?(此處能充分體現(xiàn)scope的作用,但為何如此設(shè)計(jì))
此處yield設(shè)計(jì)成靜態(tài)方法,個(gè)人認(rèn)為是想要讓continuation的維度在scope上控制,而不是在實(shí)例上控制。假如我們有一個(gè)Continuationd的List對(duì)象,list中存在scope=A與scope=B的continuation實(shí)例,分別執(zhí)行任務(wù)a和任務(wù)b,如果我需要暫停任務(wù)a,只需要list.stream().foreach(c → Continuation.yield(scopeA));
猜想:如果把list換成線程池,把continuation換成VirtualThread,是不是就能夠?qū)cope范圍的VirtualThread進(jìn)行yield?
/**
* Suspends the current continuations up to the given scope
*
* @param scope The {@link ContinuationScope} to suspend
* @return {@code true} for success; {@code false} for failure
* @throws IllegalStateException if not currently in the given {@code scope},
*/
public static boolean yield(ContinuationScope scope) {
Continuation cont = JLA.getContinuation(currentCarrierThread());
Continuation c;
// 基于Continuation實(shí)例當(dāng)前向父節(jié)點(diǎn)遍歷,直到匹配虛擬線程類型的ContinuationScope的Continuation,如果沒(méi)有匹配的Continuation會(huì)拋出異常中斷流程
// 此處其實(shí)是在校驗(yàn)當(dāng)前Continuation鏈表中是否存在需要yield的scope
for (c = cont; c != null && c.scope != scope; c = c.parent)
;
if (c == null)
throw new IllegalStateException("Not in scope " + scope);
return cont.yield0(scope, null);
}
/**
* 此方法有兩個(gè)調(diào)用的地方,一個(gè)是yield,另一個(gè)是run,其中run方法中會(huì)傳child
*
*/
private boolean yield0(ContinuationScope scope, Continuation child) {
preempted = false;
// 此處記錄需要yield 的 scope
if (scope != this.scope)
this.yieldInfo = scope;
// 該方法由c++實(shí)現(xiàn),具體:stubGenerator_aarch64.cpp → generate_cont_doYield() 方法
// 主要作用是將當(dāng)前線程的執(zhí)行狀態(tài)保存并返回到調(diào)用者,即真正實(shí)現(xiàn)yield地方
int res = doYield();
U.storeFence(); // needed to prevent certain transformations by the compiler
assert scope != this.scope || yieldInfo == null
: "scope: " + scope + " this.scope: " + this.scope + " yieldInfo: " + yieldInfo + " res: " + res;
assert yieldInfo == null || scope == this.scope || yieldInfo instanceof Integer
: "scope: " + scope + " this.scope: " + this.scope + " yieldInfo: " + yieldInfo + " res: " + res;
// 此處代碼的作用是將this的res傳遞給child,并將this.yieldInfo = null
if (child != null) { // TODO: ugly 作者的吐槽,想看后續(xù)會(huì)怎么優(yōu)化
if (res != 0) {
child.yieldInfo = res;
} else if (yieldInfo != null) {
assert yieldInfo instanceof Integer;
child.yieldInfo = yieldInfo;
} else {
child.yieldInfo = res;
}
this.yieldInfo = null;
} else {
if (res == 0 && yieldInfo != null) {
// 此處傳遞yieldInfo至鏈表最末尾的continuation
res = (Integer) yieldInfo;
}
this.yieldInfo = null;
if (res == 0)
onContinue();
else
// 非 0 則說(shuō)明 pinned ,拋異常
onPinned0(res);
}
assert yieldInfo == null;
return res == 0;
}
4、實(shí)例說(shuō)明
以下代碼為continuation 、child1、child2組成的鏈表調(diào)用,在child2進(jìn)行yield(scope)操作之后,會(huì)遍歷鏈表,將當(dāng)前節(jié)點(diǎn)yield,直至continuation實(shí)例的scope等于目標(biāo)scope為止:
public static void main(String[] args) {
ContinuationScope scope = new ContinuationScope("example1");
ContinuationScope scope2 = new ContinuationScope("example2");
ContinuationScope scope3 = new ContinuationScope("example3");
Continuation child2 = new Continuation(scope3, () -> {
System.out.println("before scope yield");
Continuation.yield(scope);
System.out.println("after scope yield");
});
Continuation child1 = new Continuation(scope2, () -> {
System.out.println("before child2 run");
child2.run();
System.out.println("after child2 run");
});
Continuation continuation = new Continuation(scope, () -> {
System.out.println("before child1 run");
child1.run();
System.out.println("after child1 run");
});
System.out.println("before run");
continuation.run();
System.out.println("before run again");
continuation.run();
System.out.println("end");
}
輸出結(jié)果:
